diff --git a/.golangci.yml b/.golangci.yml index 349ec328c3..ae2170090e 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -349,12 +349,6 @@ issues: # from the fileDescriptor set, the operation should be safe. path: private/bufpkg/bufprotosource/paths.go text: "G115:" - - linters: - - gosec - # bufimageutil is handling images and converting loop indices to int32. Since it is - # parsing from an Image, the operation should be safe. - path: private/bufpkg/bufimage/bufimageutil/bufimageutil.go - text: "G115:" - linters: - gosec # Bounds checks have been added with assertion statements to ensure safe int -> int32 diff --git a/CHANGELOG.md b/CHANGELOG.md index b37dba9269..75993f767e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,9 @@ - Add `--against-registry` flag to `buf breaking` that runs breaking checks against the latest commit on the default branch of the corresponding module in the registry. - Fix type filter with unused image dependencies for `buf generate`. +- Improve type filtering for `buf generate`. Adds the ability to exclude types with the parameter + `exclude_types` in `buf.gen.yaml` and a flag `--exclude-types` in the CLI. + Type filters may now also be specified as plugin parameters in `buf.gen.yaml`. ## [v1.50.1] - 2025-03-10 diff --git a/private/buf/bufctl/controller.go b/private/buf/bufctl/controller.go index ed5521b434..271c622521 100644 --- a/private/buf/bufctl/controller.go +++ b/private/buf/bufctl/controller.go @@ -1344,8 +1344,15 @@ func filterImage( if functionOptions.imageExcludeImports { newImage = bufimage.ImageWithoutImports(newImage) } - if len(functionOptions.imageTypes) > 0 { - newImage, err = bufimageutil.ImageFilteredByTypes(newImage, functionOptions.imageTypes...) + includeTypes := functionOptions.imageIncludeTypes + excludeTypes := functionOptions.imageExcludeTypes + if len(includeTypes) > 0 || len(excludeTypes) > 0 { + newImage, err = bufimageutil.FilterImage( + newImage, + bufimageutil.WithIncludeTypes(includeTypes...), + bufimageutil.WithExcludeTypes(excludeTypes...), + bufimageutil.WithMutateInPlace(), + ) if err != nil { return nil, err } diff --git a/private/buf/bufctl/option.go b/private/buf/bufctl/option.go index 2fd88ec9d7..8adae0c288 100644 --- a/private/buf/bufctl/option.go +++ b/private/buf/bufctl/option.go @@ -66,9 +66,15 @@ func WithImageExcludeImports(imageExcludeImports bool) FunctionOption { } } -func WithImageTypes(imageTypes []string) FunctionOption { +func WithImageIncludeTypes(imageTypes []string) FunctionOption { return func(functionOptions *functionOptions) { - functionOptions.imageTypes = imageTypes + functionOptions.imageIncludeTypes = imageTypes + } +} + +func WithImageExcludeTypes(imageExcludeTypes []string) FunctionOption { + return func(functionOptions *functionOptions) { + functionOptions.imageExcludeTypes = imageExcludeTypes } } @@ -133,7 +139,8 @@ type functionOptions struct { targetExcludePaths []string imageExcludeSourceInfo bool imageExcludeImports bool - imageTypes []string + imageIncludeTypes []string + imageExcludeTypes []string imageAsFileDescriptorSet bool configOverride string ignoreAndDisallowV1BufWorkYAMLs bool diff --git a/private/buf/bufgen/generator.go b/private/buf/bufgen/generator.go index 34802ab484..d5ac74d99f 100644 --- a/private/buf/bufgen/generator.go +++ b/private/buf/bufgen/generator.go @@ -20,12 +20,14 @@ import ( "fmt" "log/slog" "path/filepath" + "sort" connect "connectrpc.com/connect" "github.com/bufbuild/buf/private/buf/bufprotopluginexec" "github.com/bufbuild/buf/private/bufpkg/bufconfig" "github.com/bufbuild/buf/private/bufpkg/bufimage" "github.com/bufbuild/buf/private/bufpkg/bufimage/bufimagemodify" + "github.com/bufbuild/buf/private/bufpkg/bufimage/bufimageutil" "github.com/bufbuild/buf/private/bufpkg/bufprotoplugin" "github.com/bufbuild/buf/private/bufpkg/bufprotoplugin/bufprotopluginos" "github.com/bufbuild/buf/private/bufpkg/bufremoteplugin" @@ -204,72 +206,91 @@ func (g *generator) execPlugins( includeImportsOverride *bool, includeWellKnownTypesOverride *bool, ) ([]*pluginpb.CodeGeneratorResponse, error) { - imageProvider := newImageProvider(image) // Collect all of the plugin jobs so that they can be executed in parallel. jobs := make([]func(context.Context) error, 0, len(pluginConfigs)) responses := make([]*pluginpb.CodeGeneratorResponse, len(pluginConfigs)) requiredFeatures := computeRequiredFeatures(image) - remotePluginConfigTable := make(map[string][]*remotePluginExecArgs, len(pluginConfigs)) - for i, pluginConfig := range pluginConfigs { - index := i - currentPluginConfig := pluginConfig - // We're using this as a proxy for Type() == PluginConfigTypeRemote. - // - // We should be using the enum here. - remote := currentPluginConfig.RemoteHost() - if remote != "" { - remotePluginConfigTable[remote] = append( - remotePluginConfigTable[remote], - &remotePluginExecArgs{ - Index: index, - PluginConfig: currentPluginConfig, - }, + + // Group the pluginConfigs by similar properties to batch image processing. + pluginConfigsForImage := slicesext.ToIndexedValuesMap(pluginConfigs, createPluginConfigKeyForImage) + for _, indexedPluginConfigs := range pluginConfigsForImage { + image := image + pluginConfigForKey := indexedPluginConfigs[0].Value + + // Apply per-plugin filters. + includeTypes := pluginConfigForKey.IncludeTypes() + excludeTypes := pluginConfigForKey.ExcludeTypes() + if len(includeTypes) > 0 || len(excludeTypes) > 0 { + var err error + image, err = bufimageutil.FilterImage( + image, + bufimageutil.WithIncludeTypes(includeTypes...), + bufimageutil.WithExcludeTypes(excludeTypes...), ) - } else { + if err != nil { + return nil, err + } + } + + // Batch for each remote. + if remote := pluginConfigForKey.RemoteHost(); remote != "" { jobs = append(jobs, func(ctx context.Context) error { - includeImports := currentPluginConfig.IncludeImports() - if includeImportsOverride != nil { - includeImports = *includeImportsOverride - } - includeWellKnownTypes := currentPluginConfig.IncludeWKT() - if includeWellKnownTypesOverride != nil { - includeWellKnownTypes = *includeWellKnownTypesOverride - } - response, err := g.execLocalPlugin( + results, err := g.execRemotePluginsV2( ctx, container, - imageProvider, - currentPluginConfig, - includeImports, - includeWellKnownTypes, + image, + remote, + indexedPluginConfigs, + includeImportsOverride, + includeWellKnownTypesOverride, ) if err != nil { return err } - responses[index] = response + for _, result := range results { + responses[result.Index] = result.Value + } return nil }) + continue } - } - // Batch for each remote. - for remote, indexedPluginConfigs := range remotePluginConfigTable { - if len(indexedPluginConfigs) > 0 { + + // Local plugins. + var images []bufimage.Image + switch Strategy(pluginConfigForKey.Strategy()) { + case StrategyAll: + images = []bufimage.Image{image} + case StrategyDirectory: + var err error + images, err = bufimage.ImageByDir(image) + if err != nil { + return nil, err + } + default: + return nil, fmt.Errorf("unknown strategy: %v", pluginConfigForKey.Strategy()) + } + for _, indexedPluginConfig := range indexedPluginConfigs { jobs = append(jobs, func(ctx context.Context) error { - results, err := g.execRemotePluginsV2( + includeImports := indexedPluginConfig.Value.IncludeImports() + if includeImportsOverride != nil { + includeImports = *includeImportsOverride + } + includeWellKnownTypes := indexedPluginConfig.Value.IncludeWKT() + if includeWellKnownTypesOverride != nil { + includeWellKnownTypes = *includeWellKnownTypesOverride + } + response, err := g.execLocalPlugin( ctx, container, - image, - remote, - indexedPluginConfigs, - includeImportsOverride, - includeWellKnownTypesOverride, + images, + indexedPluginConfig.Value, + includeImports, + includeWellKnownTypes, ) if err != nil { return err } - for _, result := range results { - responses[result.Index] = result.CodeGeneratorResponse - } + responses[indexedPluginConfig.Index] = response return nil }) } @@ -305,15 +326,11 @@ func (g *generator) execPlugins( func (g *generator) execLocalPlugin( ctx context.Context, container app.EnvStdioContainer, - imageProvider *imageProvider, + pluginImages []bufimage.Image, pluginConfig bufconfig.GeneratePluginConfig, includeImports bool, includeWellKnownTypes bool, ) (*pluginpb.CodeGeneratorResponse, error) { - pluginImages, err := imageProvider.GetImages(Strategy(pluginConfig.Strategy())) - if err != nil { - return nil, err - } requests, err := bufimage.ImagesToCodeGeneratorRequests( pluginImages, pluginConfig.Opt(), @@ -338,37 +355,27 @@ func (g *generator) execLocalPlugin( return response, nil } -type remotePluginExecArgs struct { - Index int - PluginConfig bufconfig.GeneratePluginConfig -} - -type remotePluginExecutionResult struct { - CodeGeneratorResponse *pluginpb.CodeGeneratorResponse - Index int -} - func (g *generator) execRemotePluginsV2( ctx context.Context, container app.EnvStdioContainer, image bufimage.Image, remote string, - pluginConfigs []*remotePluginExecArgs, + indexedPluginConfigs []slicesext.Indexed[bufconfig.GeneratePluginConfig], includeImportsOverride *bool, includeWellKnownTypesOverride *bool, -) ([]*remotePluginExecutionResult, error) { - requests := make([]*registryv1alpha1.PluginGenerationRequest, len(pluginConfigs)) - for i, pluginConfig := range pluginConfigs { - includeImports := pluginConfig.PluginConfig.IncludeImports() +) ([]slicesext.Indexed[*pluginpb.CodeGeneratorResponse], error) { + requests := make([]*registryv1alpha1.PluginGenerationRequest, len(indexedPluginConfigs)) + for i, indexedPluginConfig := range indexedPluginConfigs { + includeImports := indexedPluginConfig.Value.IncludeImports() if includeImportsOverride != nil { includeImports = *includeImportsOverride } - includeWellKnownTypes := pluginConfig.PluginConfig.IncludeWKT() + includeWellKnownTypes := indexedPluginConfig.Value.IncludeWKT() if includeWellKnownTypesOverride != nil { includeWellKnownTypes = *includeWellKnownTypesOverride } request, err := getPluginGenerationRequest( - pluginConfig.PluginConfig, + indexedPluginConfig.Value, includeImports, includeWellKnownTypes, ) @@ -398,15 +405,15 @@ func (g *generator) execRemotePluginsV2( if len(responses) != len(requests) { return nil, fmt.Errorf("unexpected number of responses received, got %d, wanted %d", len(responses), len(requests)) } - result := make([]*remotePluginExecutionResult, 0, len(responses)) + result := make([]slicesext.Indexed[*pluginpb.CodeGeneratorResponse], 0, len(responses)) for i := range requests { codeGeneratorResponse := responses[i].GetResponse() if codeGeneratorResponse == nil { return nil, errors.New("expected code generator response") } - result = append(result, &remotePluginExecutionResult{ - CodeGeneratorResponse: codeGeneratorResponse, - Index: pluginConfigs[i].Index, + result = append(result, slicesext.Indexed[*pluginpb.CodeGeneratorResponse]{ + Value: codeGeneratorResponse, + Index: indexedPluginConfigs[i].Index, }) } return result, nil @@ -482,3 +489,29 @@ type generateOptions struct { func newGenerateOptions() *generateOptions { return &generateOptions{} } + +type pluginConfigKeyForImage struct { + includeTypes string // string representation of []string + excludeTypes string // string representation of []string + strategy Strategy + remoteHost string +} + +// createPluginConfigKeyForImage returns a key of the plugin config with +// a subset of properties. This is used to batch plugins that have similar +// configuration. The key is based on the following properties: +// - Types +// - ExcludeTypes +// - Strategy +// - RemoteHost +func createPluginConfigKeyForImage(pluginConfig bufconfig.GeneratePluginConfig) pluginConfigKeyForImage { + // Sort the types and excludeTypes so that the key is deterministic. + sort.Strings(pluginConfig.IncludeTypes()) + sort.Strings(pluginConfig.ExcludeTypes()) + return pluginConfigKeyForImage{ + includeTypes: fmt.Sprintf("%v", pluginConfig.IncludeTypes()), + excludeTypes: fmt.Sprintf("%v", pluginConfig.ExcludeTypes()), + strategy: Strategy(pluginConfig.Strategy()), + remoteHost: pluginConfig.RemoteHost(), + } +} diff --git a/private/buf/bufgen/image_provider.go b/private/buf/bufgen/image_provider.go deleted file mode 100644 index 171d7058c8..0000000000 --- a/private/buf/bufgen/image_provider.go +++ /dev/null @@ -1,60 +0,0 @@ -// Copyright 2020-2025 Buf Technologies, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package bufgen - -import ( - "fmt" - "sync" - - "github.com/bufbuild/buf/private/bufpkg/bufimage" -) - -// imageProvider is used to provide the images used -// when generating with a local plugin. Each plugin is -// in control of its own Strategy - we cache the -// imagesByDir so that we only have to build it once for -// all of the plugins that configure the Directory -// strategy. -type imageProvider struct { - image bufimage.Image - imagesByDir []bufimage.Image - lock sync.Mutex -} - -func newImageProvider(image bufimage.Image) *imageProvider { - return &imageProvider{ - image: image, - } -} - -func (p *imageProvider) GetImages(strategy Strategy) ([]bufimage.Image, error) { - switch strategy { - case StrategyAll: - return []bufimage.Image{p.image}, nil - case StrategyDirectory: - p.lock.Lock() - defer p.lock.Unlock() - if p.imagesByDir == nil { - var err error - p.imagesByDir, err = bufimage.ImageByDir(p.image) - if err != nil { - return nil, err - } - } - return p.imagesByDir, nil - default: - return nil, fmt.Errorf("unknown strategy: %v", strategy) - } -} diff --git a/private/buf/cmd/buf/command/build/build.go b/private/buf/cmd/buf/command/build/build.go index c3e4187270..2e70e7e9dd 100644 --- a/private/buf/cmd/buf/command/build/build.go +++ b/private/buf/cmd/buf/command/build/build.go @@ -158,7 +158,7 @@ func run( bufctl.WithTargetPaths(flags.Paths, flags.ExcludePaths), bufctl.WithImageExcludeSourceInfo(flags.ExcludeSourceInfo), bufctl.WithImageExcludeImports(flags.ExcludeImports), - bufctl.WithImageTypes(flags.Types), + bufctl.WithImageIncludeTypes(flags.Types), bufctl.WithConfigOverride(flags.Config), ) if err != nil { diff --git a/private/buf/cmd/buf/command/convert/convert.go b/private/buf/cmd/buf/command/convert/convert.go index f246f60862..aec86d1912 100644 --- a/private/buf/cmd/buf/command/convert/convert.go +++ b/private/buf/cmd/buf/command/convert/convert.go @@ -185,7 +185,11 @@ func run( resolveWellKnownType = true } if schemaImage != nil { - _, filterErr := bufimageutil.ImageFilteredByTypes(schemaImage, flags.Type) + _, filterErr := bufimageutil.FilterImage( + schemaImage, + bufimageutil.WithIncludeTypes(flags.Type), + bufimageutil.WithMutateInPlace(), + ) if errors.Is(filterErr, bufimageutil.ErrImageFilterTypeNotFound) { resolveWellKnownType = true } @@ -283,5 +287,9 @@ func wellKnownTypeImage( if err != nil { return nil, err } - return bufimageutil.ImageFilteredByTypes(image, wellKnownTypeName) + return bufimageutil.FilterImage( + image, + bufimageutil.WithIncludeTypes(wellKnownTypeName), + bufimageutil.WithMutateInPlace(), + ) } diff --git a/private/buf/cmd/buf/command/generate/generate.go b/private/buf/cmd/buf/command/generate/generate.go index ecee042db7..3beaf10098 100644 --- a/private/buf/cmd/buf/command/generate/generate.go +++ b/private/buf/cmd/buf/command/generate/generate.go @@ -50,6 +50,7 @@ const ( disableSymlinksFlagName = "disable-symlinks" typeFlagName = "type" typeDeprecatedFlagName = "include-types" + excludeTypeFlagName = "exclude-type" ) // NewCommand returns a new Command. @@ -95,6 +96,16 @@ func NewCommand( # Whether to generate code for the well-known types. # Optional. include_wkt: false + # Include only these types for this plugin. + # Optional. + types: + - "foo.v1.User" + # Exclude these types for this plugin. + # Optional. + exclude_types: + - "buf.validate.oneof" + - "buf.validate.message" + - "buf.validate.field"" # The name of a local plugin if discoverable in "${PATH}" or its path in the file system. - local: protoc-gen-es @@ -248,6 +259,10 @@ func NewCommand( types: - "foo.v1.User" - "foo.v1.UserService" + # Exclude these types. + # Optional. + exclude_types: + - "buf.validate" # Only generate code for files in these paths. # If empty, include all paths. paths: @@ -388,6 +403,7 @@ type flags struct { // want to find out what will break if we do. Types []string TypesDeprecated []string + ExcludeTypes []string // special InputHashtag string } @@ -462,6 +478,12 @@ func (f *flags) Bind(flagSet *pflag.FlagSet) { nil, "The types (package, message, enum, extension, service, method) that should be included in this image. When specified, the resulting image will only include descriptors to describe the requested types. Flag usage overrides buf.gen.yaml", ) + flagSet.StringSliceVar( + &f.ExcludeTypes, + excludeTypeFlagName, + nil, + "The types (package, message, enum, extension, service, method) that should be excluded from this image. When specified, the resulting image will omit descriptors for the specified types and remove any references to them, such as fields typed to an excluded message or enum, or custom options tied to an excluded extension. The image is first filtered by the included types, then further reduced by the excluded. Flag usage overrides buf.gen.yaml", + ) _ = flagSet.MarkDeprecated(typeDeprecatedFlagName, fmt.Sprintf("use --%s instead", typeFlagName)) _ = flagSet.MarkHidden(typeDeprecatedFlagName) } @@ -517,6 +539,7 @@ func run( flags.Paths, flags.ExcludePaths, append(flags.Types, flags.TypesDeprecated...), + flags.ExcludeTypes, ) if err != nil { return err @@ -591,6 +614,7 @@ func getInputImages( targetPathsOverride []string, excludePathsOverride []string, includeTypesOverride []string, + excludeTypesOverride []string, ) ([]bufimage.Image, error) { // If input is specified on the command line, we use that. If input is not // specified on the command line, use the default input. @@ -611,7 +635,7 @@ func getInputImages( input, bufctl.WithConfigOverride(moduleConfigOverride), bufctl.WithTargetPaths(targetPathsOverride, excludePathsOverride), - bufctl.WithImageTypes(includeTypes), + bufctl.WithImageIncludeTypes(includeTypes), ) if err != nil { return nil, err @@ -628,18 +652,21 @@ func getInputImages( if len(excludePathsOverride) > 0 { excludePaths = excludePathsOverride } - // In V2 we do not need to look at generateTypeConfig.IncludeTypes() - // because it is always nil. includeTypes := inputConfig.IncludeTypes() if len(includeTypesOverride) > 0 { includeTypes = includeTypesOverride } + excludeTypes := inputConfig.ExcludeTypes() + if len(excludeTypesOverride) > 0 { + excludeTypes = excludeTypesOverride + } inputImage, err := controller.GetImageForInputConfig( ctx, inputConfig, bufctl.WithConfigOverride(moduleConfigOverride), bufctl.WithTargetPaths(targetPaths, excludePaths), - bufctl.WithImageTypes(includeTypes), + bufctl.WithImageIncludeTypes(includeTypes), + bufctl.WithImageExcludeTypes(excludeTypes), ) if err != nil { return nil, err diff --git a/private/buf/cmd/buf/command/generate/generate_test.go b/private/buf/cmd/buf/command/generate/generate_test.go index ea23236f0b..f70fc98c85 100644 --- a/private/buf/cmd/buf/command/generate/generate_test.go +++ b/private/buf/cmd/buf/command/generate/generate_test.go @@ -317,6 +317,29 @@ inputs: "--type", "b.v1.Bar", ) + // buf.gen.yaml has types and exclude_types on inputs and plugins + testRunTypeArgs(t, map[string][]byte{ + filepath.Join("gen", "a", "v1", "a.top-level-type-names.yaml"): []byte(`messages: + - a.v1.Foo +`), + filepath.Join("gen", "b", "v1", "b.top-level-type-names.yaml"): []byte(`messages: + - b.v1.Baz +`), + }, + "--template", + filepath.Join("testdata", "v2", "types", "buf.gen.yaml"), + ) + // --exclude-type override + testRunTypeArgs(t, map[string][]byte{ + filepath.Join("gen", "a", "v1", "a.top-level-type-names.yaml"): []byte(`messages: + - a.v1.Foo +`), + }, + "--template", + filepath.Join("testdata", "v2", "types", "buf.gen.yaml"), + "--exclude-type", + "b.v1.Baz", + ) } func TestOutputFlag(t *testing.T) { diff --git a/private/buf/cmd/buf/command/generate/testdata/v2/types/a/v1/a.proto b/private/buf/cmd/buf/command/generate/testdata/v2/types/a/v1/a.proto new file mode 100644 index 0000000000..f5466e41c7 --- /dev/null +++ b/private/buf/cmd/buf/command/generate/testdata/v2/types/a/v1/a.proto @@ -0,0 +1,23 @@ +syntax = "proto3"; + +package a.v1; + +import "b/v1/b.proto"; +import "pkg/v1/options.proto"; + +message Foo { + option (pkg.v1.message_foo).foo = "str"; + message Bar { + option (pkg.v1.message_bar).bar = "str"; + string bar = 1; + } + Bar nested_bar = 1; + b.v1.Baz baz = 2; +} + +message Empty {} + +message FooBar { + Foo foo = 1; + Foo.Bar bar = 2; +} diff --git a/private/buf/cmd/buf/command/generate/testdata/v2/types/b/v1/b.proto b/private/buf/cmd/buf/command/generate/testdata/v2/types/b/v1/b.proto new file mode 100644 index 0000000000..2907baac9e --- /dev/null +++ b/private/buf/cmd/buf/command/generate/testdata/v2/types/b/v1/b.proto @@ -0,0 +1,9 @@ +syntax = "proto3"; + +package b.v1; + +import "pkg/v1/options.proto"; + +message Baz { + option (pkg.v1.message_baz) = "str"; +} diff --git a/private/buf/cmd/buf/command/generate/testdata/v2/types/buf.gen.invalid.yaml b/private/buf/cmd/buf/command/generate/testdata/v2/types/buf.gen.invalid.yaml new file mode 100644 index 0000000000..97827b3096 --- /dev/null +++ b/private/buf/cmd/buf/command/generate/testdata/v2/types/buf.gen.invalid.yaml @@ -0,0 +1,11 @@ +version: v2 +plugins: + - local: protoc-gen-top-level-type-names-yaml + out: gen + strategy: all +inputs: + - directory: ./testdata/v2/types + types: + - "a.v1.Foo.Bar" + exclude_types: + - "a.v1.Foo" diff --git a/private/buf/cmd/buf/command/generate/testdata/v2/types/buf.gen.yaml b/private/buf/cmd/buf/command/generate/testdata/v2/types/buf.gen.yaml new file mode 100644 index 0000000000..76e3f7cdd8 --- /dev/null +++ b/private/buf/cmd/buf/command/generate/testdata/v2/types/buf.gen.yaml @@ -0,0 +1,23 @@ +version: v2 +managed: + enabled: true + override: + - file_option: java_package_prefix + value: net +plugins: + - local: protoc-gen-top-level-type-names-yaml + out: gen + strategy: all + types: + - "a.v1.Foo" + exclude_types: + - "a.v1.Foo.Bar" + - "pkg.v1.message_foo" + - "pkg.v1.message_bar" +inputs: + - directory: ./testdata/v2/types + types: + - "a.v1.FooBar" + exclude_types: + - "a.v1.Empty" + - "pkg.v1.message_baz" diff --git a/private/buf/cmd/buf/command/generate/testdata/v2/types/buf.yaml b/private/buf/cmd/buf/command/generate/testdata/v2/types/buf.yaml new file mode 100644 index 0000000000..b4d4e478b7 --- /dev/null +++ b/private/buf/cmd/buf/command/generate/testdata/v2/types/buf.yaml @@ -0,0 +1 @@ +version: v2 diff --git a/private/buf/cmd/buf/command/generate/testdata/v2/types/pkg/v1/options.proto b/private/buf/cmd/buf/command/generate/testdata/v2/types/pkg/v1/options.proto new file mode 100644 index 0000000000..61e186b7ef --- /dev/null +++ b/private/buf/cmd/buf/command/generate/testdata/v2/types/pkg/v1/options.proto @@ -0,0 +1,19 @@ +syntax = "proto3"; + +package pkg.v1; + +import "google/protobuf/descriptor.proto"; + +message OptionFoo { + string foo = 1; +} + +message OptionBar { + string bar = 1; +} + +extend google.protobuf.MessageOptions { + optional OptionFoo message_foo = 50000; + optional OptionBar message_bar = 50001; + optional string message_baz = 50002; +} diff --git a/private/bufpkg/bufconfig/buf_gen_yaml_file.go b/private/bufpkg/bufconfig/buf_gen_yaml_file.go index 1ce185fbef..e2c40a7bcb 100644 --- a/private/bufpkg/bufconfig/buf_gen_yaml_file.go +++ b/private/bufpkg/bufconfig/buf_gen_yaml_file.go @@ -514,6 +514,11 @@ type externalGeneratePluginConfigV2 struct { IncludeWKT bool `json:"include_wkt,omitempty" yaml:"include_wkt,omitempty"` // Strategy is only valid with ProtoBuiltin and Local. Strategy *string `json:"strategy,omitempty" yaml:"strategy,omitempty"` + + // Types is a list of types to include in the image. + Types []string `json:"types,omitempty" yaml:"types,omitempty"` + // ExcludeTypes removes types from the image. + ExcludeTypes []string `json:"exclude_types,omitempty" yaml:"exclude_types,omitempty"` } // externalGenerateManagedConfigV2 represents the managed mode config in a v2 buf.gen.yaml file. @@ -564,8 +569,9 @@ type externalInputConfigV2 struct { TextImage *string `json:"text_image,omitempty" yaml:"text_image,omitempty"` YAMLImage *string `json:"yaml_image,omitempty" yaml:"yaml_image,omitempty"` GitRepo *string `json:"git_repo,omitempty" yaml:"git_repo,omitempty"` - // Types, TargetPaths and ExcludePaths are available for all formats. + // Types, ExcludeTypes, TargetPaths and ExcludePaths are available for all formats. Types []string `json:"types,omitempty" yaml:"types,omitempty"` + ExcludeTypes []string `json:"exclude_types,omitempty" yaml:"exclude_types,omitempty"` TargetPaths []string `json:"paths,omitempty" yaml:"paths,omitempty"` ExcludePaths []string `json:"exclude_paths,omitempty" yaml:"exclude_paths,omitempty"` // The following options are available depending on input format. diff --git a/private/bufpkg/bufconfig/buf_gen_yaml_file_test.go b/private/bufpkg/bufconfig/buf_gen_yaml_file_test.go index 0ac8117afd..e178233a42 100644 --- a/private/bufpkg/bufconfig/buf_gen_yaml_file_test.go +++ b/private/bufpkg/bufconfig/buf_gen_yaml_file_test.go @@ -180,6 +180,12 @@ plugins: strategy: all include_imports: true include_wkt: true + types: + - "foo.v1.User" + exclude_types: + - buf.validate.oneof + - buf.validate.message + - buf.validate.field inputs: - git_repo: github.com/acme/weather branch: dev @@ -189,6 +195,8 @@ inputs: types: - "foo.v1.User" - "foo.v1.UserService" + exclude_types: + - buf.validate.oneof paths: - a/b/c - a/b/d diff --git a/private/bufpkg/bufconfig/generate_plugin_config.go b/private/bufpkg/bufconfig/generate_plugin_config.go index 3208cf0386..97c9e77d97 100644 --- a/private/bufpkg/bufconfig/generate_plugin_config.go +++ b/private/bufpkg/bufconfig/generate_plugin_config.go @@ -112,6 +112,10 @@ type GeneratePluginConfig interface { // // This is not empty only when the plugin is remote. Revision() int + // IncludeTypes returns the types to include. + IncludeTypes() []string + // ExcludeTypes returns the types to exclude. + ExcludeTypes() []string isGeneratePluginConfig() } @@ -123,6 +127,8 @@ func NewRemoteGeneratePluginConfig( opt []string, includeImports bool, includeWKT bool, + includeTypes []string, + excludeTypes []string, revision int, ) (GeneratePluginConfig, error) { return newRemoteGeneratePluginConfig( @@ -131,6 +137,8 @@ func NewRemoteGeneratePluginConfig( opt, includeImports, includeWKT, + includeTypes, + excludeTypes, revision, ) } @@ -142,6 +150,8 @@ func NewLocalOrProtocBuiltinGeneratePluginConfig( opt []string, includeImports bool, includeWKT bool, + includeTypes []string, + excludeTypes []string, strategy *GenerateStrategy, ) (GeneratePluginConfig, error) { return newLocalOrProtocBuiltinGeneratePluginConfig( @@ -150,6 +160,8 @@ func NewLocalOrProtocBuiltinGeneratePluginConfig( opt, includeImports, includeWKT, + includeTypes, + excludeTypes, strategy, ) } @@ -161,6 +173,8 @@ func NewLocalGeneratePluginConfig( opt []string, includeImports bool, includeWKT bool, + types []string, + excludeTypes []string, strategy *GenerateStrategy, path []string, ) (GeneratePluginConfig, error) { @@ -170,6 +184,8 @@ func NewLocalGeneratePluginConfig( opt, includeImports, includeWKT, + types, + excludeTypes, strategy, path, ) @@ -183,6 +199,8 @@ func NewProtocBuiltinGeneratePluginConfig( opt []string, includeImports bool, includeWKT bool, + includeTypes []string, + excludeTypes []string, strategy *GenerateStrategy, protocPath []string, ) (GeneratePluginConfig, error) { @@ -192,6 +210,8 @@ func NewProtocBuiltinGeneratePluginConfig( opt, includeImports, includeWKT, + includeTypes, + excludeTypes, strategy, protocPath, ) @@ -227,6 +247,8 @@ type generatePluginConfig struct { opts []string includeImports bool includeWKT bool + includeTypes []string + excludeTypes []string strategy *GenerateStrategy path []string protocPath []string @@ -258,6 +280,8 @@ func newGeneratePluginConfigFromExternalV1Beta1( opt, false, false, + nil, + nil, strategy, []string{externalConfig.Path}, ) @@ -268,6 +292,8 @@ func newGeneratePluginConfigFromExternalV1Beta1( opt, false, false, + nil, + nil, strategy, ) } @@ -327,6 +353,8 @@ func newGeneratePluginConfigFromExternalV1( opt, false, false, + nil, + nil, externalConfig.Revision, ) } @@ -339,6 +367,8 @@ func newGeneratePluginConfigFromExternalV1( opt, false, false, + nil, + nil, strategy, path, ) @@ -350,6 +380,8 @@ func newGeneratePluginConfigFromExternalV1( opt, false, false, + nil, + nil, strategy, protocPath, ) @@ -362,6 +394,8 @@ func newGeneratePluginConfigFromExternalV1( opt, false, false, + nil, + nil, strategy, ) } @@ -418,6 +452,8 @@ func newGeneratePluginConfigFromExternalV2( opt, externalConfig.IncludeImports, externalConfig.IncludeWKT, + externalConfig.Types, + externalConfig.ExcludeTypes, revision, ) case externalConfig.Local != nil: @@ -438,6 +474,8 @@ func newGeneratePluginConfigFromExternalV2( opt, externalConfig.IncludeImports, externalConfig.IncludeWKT, + externalConfig.Types, + externalConfig.ExcludeTypes, parsedStrategy, path, ) @@ -455,6 +493,8 @@ func newGeneratePluginConfigFromExternalV2( opt, externalConfig.IncludeImports, externalConfig.IncludeWKT, + externalConfig.Types, + externalConfig.ExcludeTypes, parsedStrategy, protocPath, ) @@ -469,6 +509,8 @@ func newRemoteGeneratePluginConfig( opt []string, includeImports bool, includeWKT bool, + includeTypes []string, + excludeTypes []string, revision int, ) (*generatePluginConfig, error) { if includeWKT && !includeImports { @@ -490,6 +532,8 @@ func newRemoteGeneratePluginConfig( opts: opt, includeImports: includeImports, includeWKT: includeWKT, + includeTypes: includeTypes, + excludeTypes: excludeTypes, }, nil } @@ -499,6 +543,8 @@ func newLocalOrProtocBuiltinGeneratePluginConfig( opt []string, includeImports bool, includeWKT bool, + includeTypes []string, + excludeTypes []string, strategy *GenerateStrategy, ) (*generatePluginConfig, error) { if includeWKT && !includeImports { @@ -512,6 +558,8 @@ func newLocalOrProtocBuiltinGeneratePluginConfig( opts: opt, includeImports: includeImports, includeWKT: includeWKT, + includeTypes: includeTypes, + excludeTypes: excludeTypes, }, nil } @@ -521,6 +569,8 @@ func newLocalGeneratePluginConfig( opt []string, includeImports bool, includeWKT bool, + includeTypes []string, + excludeTypes []string, strategy *GenerateStrategy, path []string, ) (*generatePluginConfig, error) { @@ -539,6 +589,8 @@ func newLocalGeneratePluginConfig( opts: opt, includeImports: includeImports, includeWKT: includeWKT, + includeTypes: includeTypes, + excludeTypes: excludeTypes, }, nil } @@ -548,6 +600,8 @@ func newProtocBuiltinGeneratePluginConfig( opt []string, includeImports bool, includeWKT bool, + includeTypes []string, + excludeTypes []string, strategy *GenerateStrategy, protocPath []string, ) (*generatePluginConfig, error) { @@ -563,6 +617,8 @@ func newProtocBuiltinGeneratePluginConfig( strategy: strategy, includeImports: includeImports, includeWKT: includeWKT, + includeTypes: includeTypes, + excludeTypes: excludeTypes, }, nil } @@ -590,6 +646,14 @@ func (p *generatePluginConfig) IncludeWKT() bool { return p.includeWKT } +func (p *generatePluginConfig) IncludeTypes() []string { + return p.includeTypes +} + +func (p *generatePluginConfig) ExcludeTypes() []string { + return p.excludeTypes +} + func (p *generatePluginConfig) Strategy() GenerateStrategy { if p.strategy == nil { return GenerateStrategyDirectory diff --git a/private/bufpkg/bufconfig/input_config.go b/private/bufpkg/bufconfig/input_config.go index e4cdc95f0c..acf01c9419 100644 --- a/private/bufpkg/bufconfig/input_config.go +++ b/private/bufpkg/bufconfig/input_config.go @@ -162,6 +162,12 @@ type InputConfig interface { ExcludePaths() []string // IncludeTypes returns the types to generate. An empty slice means to generate for all types. IncludeTypes() []string + // ExcludeTypes returns the types to exclude. An empty slice means to exclude no types. + // First ExcludeTypes are removed from the image. + // Then IncludeTypes are traversed to compute the full set of types in the schema. + // Not only are the types removed, but all references to the types must also be + // removed in order for the resulting schema to be valid. + ExcludeTypes() []string isInputConfig() } @@ -343,6 +349,7 @@ type inputConfig struct { recurseSubmodules bool includePackageFiles bool includeTypes []string + excludeTypes []string targetPaths []string excludePaths []string } @@ -401,6 +408,7 @@ func newInputConfigFromExternalV2(externalConfig externalInputConfigV2) (InputCo inputConfig.inputConfigType = inputConfigType // Types, TargetPaths, and ExcludePaths. inputConfig.includeTypes = externalConfig.Types + inputConfig.excludeTypes = externalConfig.ExcludeTypes inputConfig.targetPaths = externalConfig.TargetPaths inputConfig.excludePaths = externalConfig.ExcludePaths // Options depending on input format. @@ -516,6 +524,10 @@ func (i *inputConfig) IncludeTypes() []string { return i.includeTypes } +func (i *inputConfig) ExcludeTypes() []string { + return i.excludeTypes +} + func (i *inputConfig) isInputConfig() {} func newExternalInputConfigV2FromInputConfig( diff --git a/private/bufpkg/bufimage/bufimageutil/bufimageutil.go b/private/bufpkg/bufimage/bufimageutil/bufimageutil.go index d939a2ab15..3ad9b2eb9a 100644 --- a/private/bufpkg/bufimage/bufimageutil/bufimageutil.go +++ b/private/bufpkg/bufimage/bufimageutil/bufimageutil.go @@ -90,23 +90,85 @@ func WithExcludeKnownExtensions() ImageFilterOption { } } -// WithAllowFilterByImportedType returns an option for ImageFilteredByTypesWithOptions -// that allows a named filter type to be in an imported file or module. Without this +// WithAllowIncludeOfImportedType returns an option for ImageFilteredByTypesWithOptions +// that allows a named included type to be in an imported file or module. Without this // option, only types defined directly in the image to be filtered are allowed. -func WithAllowFilterByImportedType() ImageFilterOption { +// Excluded types are always allowed to be in imported files or modules. +func WithAllowIncludeOfImportedType() ImageFilterOption { return func(opts *imageFilterOptions) { opts.allowImportedTypes = true } } -// ImageFilteredByTypes returns a minimal image containing only the descriptors -// required to define those types. The resulting contains only files in which -// those descriptors and their transitive closure of required descriptors, with -// each file only contains the minimal required types and imports. +// WithIncludeTypes returns an option for ImageFilteredByTypesWithOptions that specifies +// the set of types that should be included in the filtered image. // -// Although this returns a new [bufimage.Image], it mutates the original image's -// underlying file's [descriptorpb.FileDescriptorProto]. So the old image should -// not continue to be used. +// May be provided multiple times. The type names should be fully qualified. +// For example, "google.protobuf.Any" or "buf.validate". Types may be nested, +// and can be any package, message, enum, extension, service or method name. +// +// If the type does not exist in the image, an error wrapping +// [ErrImageFilterTypeNotFound] will be returned. +func WithIncludeTypes(typeNames ...string) ImageFilterOption { + return func(opts *imageFilterOptions) { + if len(typeNames) > 0 && opts.includeTypes == nil { + opts.includeTypes = make(map[string]struct{}, len(typeNames)) + } + for _, typeName := range typeNames { + opts.includeTypes[typeName] = struct{}{} + } + } +} + +// WithExcludeTypes returns an option for ImageFilteredByTypesWithOptions that +// specifies the set of types that should be excluded from the filtered image. +// +// May be provided multiple times. The type names should be fully qualified. +// For example, "google.protobuf.Any" or "buf.validate". Types may be nested, +// and can be any package, message, enum, extension, service or method name. +// +// If the type does not exist in the image, an error wrapping +// [ErrImageFilterTypeNotFound] will be returned. +func WithExcludeTypes(typeNames ...string) ImageFilterOption { + return func(opts *imageFilterOptions) { + if len(typeNames) > 0 && opts.excludeTypes == nil { + opts.excludeTypes = make(map[string]struct{}, len(typeNames)) + } + for _, typeName := range typeNames { + opts.excludeTypes[typeName] = struct{}{} + } + } +} + +// WithMutateInPlace returns an option for ImageFilteredByTypesWithOptions that specifies +// that the filtered image should be mutated in place. This option is useful when the +// unfiltered image is no longer needed and the caller wants to avoid the overhead of +// copying the image. +func WithMutateInPlace() ImageFilterOption { + return func(opts *imageFilterOptions) { + opts.mutateInPlace = true + } +} + +// FilterImage returns a minimal image containing only the descriptors +// required to define the set of types provided by the filter options. If no +// filter options are provided, the original image is returned. +// +// The filtered image will contain only the files that contain the definitions of +// the specified types, and their transitive dependencies. If a file is no longer +// required, it will be removed from the image. Only the minimal set of types +// required to define the specified types will be included in the filtered image. +// +// Excluded types and options are not included in the filtered image. If an +// included type transitively depends on the excluded type, the descriptor will +// be altered to remove the dependency. +// +// This returns a new [bufimage.Image] that is a shallow copy of the underlying +// [descriptorpb.FileDescriptorProto]s of the original. The new image may +// therefore share state with the original image, so it should not be modified. +// If the original image is no longer needed, it should be discarded. To mutate +// the original image, use the [WithMutateInPlace] option. Otherwise, to avoid +// sharing of state clone the image before filtering. // // A descriptor is said to require another descriptor if the dependent // descriptor is needed to accurately and completely describe that descriptor. @@ -167,219 +229,20 @@ func WithAllowFilterByImportedType() ImageFilterOption { // files: [foo.proto, bar.proto] // messages: [pkg.Baz, other.Quux, other.Qux] // extensions: [other.my_option] -func ImageFilteredByTypes(image bufimage.Image, types ...string) (bufimage.Image, error) { - return ImageFilteredByTypesWithOptions(image, types) -} - -// ImageFilteredByTypesWithOptions returns a minimal image containing only the descriptors -// required to define those types. See ImageFilteredByTypes for more details. This version -// allows for customizing the behavior with options. -func ImageFilteredByTypesWithOptions(image bufimage.Image, types []string, opts ...ImageFilterOption) (bufimage.Image, error) { - options := newImageFilterOptions() - for _, o := range opts { - o(options) +func FilterImage(image bufimage.Image, options ...ImageFilterOption) (bufimage.Image, error) { + if len(options) == 0 { + return image, nil } - - imageIndex, err := newImageIndexForImage(image, options) - if err != nil { - return nil, err + filterOptions := newImageFilterOptions() + for _, option := range options { + option(filterOptions) } - // Check types exist - startingDescriptors := make([]namedDescriptor, 0, len(types)) - var startingPackages []*protoPackage - for _, typeName := range types { - // TODO: consider supporting a glob syntax of some kind, to do more advanced pattern - // matching, such as ability to get a package AND all of its sub-packages. - startingDescriptor, ok := imageIndex.ByName[typeName] - if ok { - // It's a type name - typeInfo := imageIndex.ByDescriptor[startingDescriptor] - if image.GetFile(typeInfo.file).IsImport() && !options.allowImportedTypes { - return nil, fmt.Errorf("filtering by type %q: %w", typeName, ErrImageFilterTypeIsImport) - } - startingDescriptors = append(startingDescriptors, startingDescriptor) - continue - } - // It could be a package name - pkg, ok := imageIndex.Packages[typeName] - if !ok { - // but it's not... - return nil, fmt.Errorf("filtering by type %q: %w", typeName, ErrImageFilterTypeNotFound) - } - if !options.allowImportedTypes { - // if package includes only imported files, then reject - onlyImported := true - for _, file := range pkg.files { - if !file.IsImport() { - onlyImported = false - break - } - } - if onlyImported { - return nil, fmt.Errorf("filtering by type %q: %w", typeName, ErrImageFilterTypeIsImport) - } - } - startingPackages = append(startingPackages, pkg) - } - // Find all types to include in filtered image. - closure := newTransitiveClosure() - for _, startingPackage := range startingPackages { - if err := closure.addPackage(startingPackage, imageIndex, options); err != nil { - return nil, err - } - } - for _, startingDescriptor := range startingDescriptors { - if err := closure.addElement(startingDescriptor, "", false, imageIndex, options); err != nil { - return nil, err - } - } - // After all types are added, add their known extensions - if err := closure.addExtensions(imageIndex, options); err != nil { - return nil, err - } - // Create a new image with only the required descriptors. - var includedFiles []bufimage.ImageFile - for _, imageFile := range image.Files() { - _, ok := closure.files[imageFile.Path()] - if !ok { - continue - } - imageFileDescriptor := imageFile.FileDescriptorProto() - - importsRequired := closure.imports[imageFile.Path()] - // If the file has source code info, we need to remap paths to correctly - // update this info for the elements retained after filtering. - var sourcePathRemapper *sourcePathsRemapTrie - if len(imageFileDescriptor.SourceCodeInfo.GetLocation()) > 0 { - sourcePathRemapper = &sourcePathsRemapTrie{} - } - // We track the source path as we go through the model, so that we can - // mark paths as moved or deleted. Whenever an element is deleted, any - // subsequent elements of the same type in the same scope have are "moved", - // because their index is shifted down. - basePath := make([]int32, 1, 16) - basePath[0] = fileDependencyTag - // While employing - // https://github.com/golang/go/wiki/SliceTricks#filter-in-place, - // also keep a record of which index moved where, so we can fixup - // the file's WeakDependency field. - indexFromTo := make(map[int32]int32) - indexTo := 0 - // Only handle imports and dependencies if there are any. - for indexFrom, importPath := range imageFileDescriptor.GetDependency() { - path := append(basePath, int32(indexFrom)) - // We check if the import path exists among required imports. If yes, we - // move and then delete from required imports as we go. - if importsRequired != nil && importsRequired.index(importPath) != -1 { - sourcePathRemapper.markMoved(path, int32(indexTo)) - indexFromTo[int32(indexFrom)] = int32(indexTo) - imageFileDescriptor.Dependency[indexTo] = importPath - indexTo++ - // delete them as we go, so we know which ones weren't in the list - importsRequired.delete(importPath) - } else { - // Path did not exist in required imports, we mark as deleted. - sourcePathRemapper.markDeleted(path) - } - } - imageFileDescriptor.Dependency = imageFileDescriptor.Dependency[:indexTo] - - // Add any other imports (which may not have been in the list because - // they were picked up via a public import). The filtered files will not - // use public imports. - // The imports are added in the order they are encountered when importing - // to maintain a deterministic ordering. - if importsRequired != nil { - imageFileDescriptor.Dependency = append(imageFileDescriptor.Dependency, importsRequired.keys()...) - } - - imageFileDescriptor.PublicDependency = nil - sourcePathRemapper.markDeleted([]int32{filePublicDependencyTag}) - - basePath = basePath[:1] - basePath[0] = fileWeakDependencyTag - i := 0 - for _, indexFrom := range imageFileDescriptor.WeakDependency { - path := append(basePath, indexFrom) - if indexTo, ok := indexFromTo[indexFrom]; ok { - sourcePathRemapper.markMoved(path, indexTo) - imageFileDescriptor.WeakDependency[i] = indexTo - i++ - } else { - sourcePathRemapper.markDeleted(path) - } - } - imageFileDescriptor.WeakDependency = imageFileDescriptor.WeakDependency[:i] - - if _, ok := closure.completeFiles[imageFile.Path()]; !ok { - // if not keeping entire file, filter contents now - basePath = basePath[:0] - imageFileDescriptor.MessageType = trimMessageDescriptors(imageFileDescriptor.MessageType, closure.elements, sourcePathRemapper, append(basePath, fileMessagesTag)) - imageFileDescriptor.EnumType = trimSlice(imageFileDescriptor.EnumType, closure.elements, sourcePathRemapper, append(basePath, fileEnumsTag)) - // TODO: We could end up removing all extensions from a particular extend block - // but we then don't mark that extend block's source code info for deletion. This - // is because extend blocks don't have distinct paths -- we have to actually look - // at the span information to determine which extensions correspond to which blocks - // to decide which blocks to remove. That is possible, but non-trivial, and it's - // unclear if the "juice is worth the squeeze", so we leave it. The best we do is - // to remove comments for extend blocks when there are NO extensions. - extsPath := append(basePath, fileExtensionsTag) - imageFileDescriptor.Extension = trimSlice(imageFileDescriptor.Extension, closure.elements, sourcePathRemapper, extsPath) - if len(imageFileDescriptor.Extension) == 0 { - sourcePathRemapper.markDeleted(extsPath) - } - svcsPath := append(basePath, fileServicesTag) - // We must iterate through the services *before* we trim the slice. That way the - // index we see is for the "old path", which we need to know to mark elements as - // moved or deleted with the sourcePathRemapper. - for index, serviceDescriptor := range imageFileDescriptor.Service { - if _, ok := closure.elements[serviceDescriptor]; !ok { - continue - } - methodPath := append(svcsPath, int32(index), serviceMethodsTag) - serviceDescriptor.Method = trimSlice(serviceDescriptor.Method, closure.elements, sourcePathRemapper, methodPath) - } - imageFileDescriptor.Service = trimSlice(imageFileDescriptor.Service, closure.elements, sourcePathRemapper, svcsPath) - } - - if len(imageFileDescriptor.SourceCodeInfo.GetLocation()) > 0 { - // Now the sourcePathRemapper has been fully populated for all of the deletions - // and moves above. So we can use it to reconstruct the source code info slice - // of locations. - i := 0 - for _, location := range imageFileDescriptor.SourceCodeInfo.Location { - // This function returns newPath==nil if the element at the given path - // was marked for deletion (so this location should be omitted). - newPath, noComment := sourcePathRemapper.newPath(location.Path) - if newPath != nil { - imageFileDescriptor.SourceCodeInfo.Location[i] = location - location.Path = newPath - if noComment { - location.LeadingDetachedComments = nil - location.LeadingComments = nil - location.TrailingComments = nil - } - i++ - } - } - imageFileDescriptor.SourceCodeInfo.Location = imageFileDescriptor.SourceCodeInfo.Location[:i] - } - imageFile, err = bufimage.NewImageFile( - imageFileDescriptor, - imageFile.FullName(), - imageFile.CommitID(), - imageFile.ExternalPath(), - imageFile.LocalPath(), - imageFile.IsImport(), - imageFile.IsSyntaxUnspecified(), - nil, // There are no unused dependencies. - ) - if err != nil { - return nil, err - } - includedFiles = append(includedFiles, imageFile) + // Check for defaults that would result in no filtering. + if len(filterOptions.excludeTypes) == 0 && + len(filterOptions.includeTypes) == 0 { + return image, nil } - return bufimage.NewImage(includedFiles) + return filterImage(image, filterOptions) } // StripSourceRetentionOptions strips any options with a retention of "source" from @@ -397,87 +260,12 @@ func StripSourceRetentionOptions(image bufimage.Image) (bufimage.Image, error) { return bufimage.NewImage(updatedFiles) } -// trimMessageDescriptors removes (nested) messages and nested enums from a slice -// of message descriptors if their type names are not found in the toKeep map. -func trimMessageDescriptors( - in []*descriptorpb.DescriptorProto, - toKeep map[namedDescriptor]closureInclusionMode, - sourcePathRemapper *sourcePathsRemapTrie, - pathSoFar []int32, -) []*descriptorpb.DescriptorProto { - // We must iterate through the messages *before* we trim the slice. That way the - // index we see is for the "old path", which we need to know to mark elements as - // moved or deleted with the sourcePathRemapper. - for index, messageDescriptor := range in { - path := append(pathSoFar, int32(index)) - mode, ok := toKeep[messageDescriptor] - if !ok { - continue - } - if mode == inclusionModeEnclosing { - // if this is just an enclosing element, we only care about it as a namespace for - // other types and don't care about the rest of its contents - messageDescriptor.Field = nil - messageDescriptor.OneofDecl = nil - messageDescriptor.ExtensionRange = nil - messageDescriptor.ReservedRange = nil - messageDescriptor.ReservedName = nil - sourcePathRemapper.markNoComment(path) - sourcePathRemapper.markDeleted(append(path, messageFieldsTag)) - sourcePathRemapper.markDeleted(append(path, messageOneofsTag)) - sourcePathRemapper.markDeleted(append(path, messageExtensionRangesTag)) - sourcePathRemapper.markDeleted(append(path, messageReservedRangesTag)) - sourcePathRemapper.markDeleted(append(path, messageReservedNamesTag)) - } - messageDescriptor.NestedType = trimMessageDescriptors(messageDescriptor.NestedType, toKeep, sourcePathRemapper, append(path, messageNestedMessagesTag)) - messageDescriptor.EnumType = trimSlice(messageDescriptor.EnumType, toKeep, sourcePathRemapper, append(path, messageEnumsTag)) - // TODO: We could end up removing all extensions from a particular extend block - // but we then don't mark that extend block's source code info for deletion. The - // best we do is to remove comments for extend blocks when there are NO extensions. - // See comment above for file extensions for more info. - extsPath := append(path, messageExtensionsTag) - messageDescriptor.Extension = trimSlice(messageDescriptor.Extension, toKeep, sourcePathRemapper, extsPath) - if len(messageDescriptor.Extension) == 0 { - sourcePathRemapper.markDeleted(extsPath) - } - } - return trimSlice(in, toKeep, sourcePathRemapper, pathSoFar) -} - -// trimSlice removes elements from a slice of descriptors if they are -// not present in the given map. -func trimSlice[T namedDescriptor]( - in []T, - toKeep map[namedDescriptor]closureInclusionMode, - sourcePathRemapper *sourcePathsRemapTrie, - pathSoFar []int32, -) []T { - i := 0 - for index, descriptor := range in { - path := append(pathSoFar, int32(index)) - if _, ok := toKeep[descriptor]; ok { - sourcePathRemapper.markMoved(path, int32(i)) - in[i] = descriptor - i++ - } else { - sourcePathRemapper.markDeleted(path) - } - } - return in[:i] -} - // transitiveClosure accumulates the elements, files, and needed imports for a // subset of an image. When an element is added to the closure, all of its // dependencies are recursively added. type transitiveClosure struct { // The elements included in the transitive closure. elements map[namedDescriptor]closureInclusionMode - // The set of files that contain all items in elements. - files map[string]struct{} - // Any files that are part of the closure in their entirety (due to an - // entire package being included). The above fields are used to filter the - // contents of files. But files named in this set will not be filtered. - completeFiles map[string]struct{} // The ordered set of imports for each file. This allows for re-writing imports // for files whose contents have been pruned. imports map[string]*orderedImports @@ -486,8 +274,12 @@ type transitiveClosure struct { type closureInclusionMode int const ( + // Element is explicitly excluded from the closure. + inclusionModeExcluded = closureInclusionMode(iota - 1) + // Element is not yet known to be included or excluded. + inclusionModeUnknown // Element is included in closure because it is directly reachable from a root. - inclusionModeExplicit = closureInclusionMode(iota) + inclusionModeExplicit // Element is included in closure because it is a message or service that // *contains* an explicitly included element but is not itself directly // reachable. @@ -502,52 +294,132 @@ const ( func newTransitiveClosure() *transitiveClosure { return &transitiveClosure{ - elements: map[namedDescriptor]closureInclusionMode{}, - files: map[string]struct{}{}, - completeFiles: map[string]struct{}{}, - imports: map[string]*orderedImports{}, + elements: map[namedDescriptor]closureInclusionMode{}, + imports: map[string]*orderedImports{}, } } -func (t *transitiveClosure) addImport(fromPath, toPath string) { - if fromPath == toPath { - return // no need for a file to import itself - } - imps := t.imports[fromPath] - if imps == nil { - imps = newOrderedImports() - t.imports[fromPath] = imps +func (t *transitiveClosure) hasType( + descriptor namedDescriptor, + options *imageFilterOptions, +) (isIncluded bool) { + switch mode := t.elements[descriptor]; mode { + case inclusionModeExplicit, inclusionModeImplicit, inclusionModeEnclosing: + return true + case inclusionModeExcluded: + return false + case inclusionModeUnknown: + // True if no includes are specified. + return options.includeTypes == nil + default: + return false } - imps.add(toPath) } -func (t *transitiveClosure) addFile(file string, imageIndex *imageIndex, opts *imageFilterOptions) error { - if _, ok := t.files[file]; ok { - return nil // already added +func (t *transitiveClosure) hasOption( + fieldDescriptor protoreflect.FieldDescriptor, + imageIndex *imageIndex, + options *imageFilterOptions, +) (isIncluded bool) { + if !fieldDescriptor.IsExtension() { + return true + } + if !options.includeCustomOptions { + return false + } + fullName := fieldDescriptor.FullName() + descriptor := imageIndex.ByName[fullName].element + switch mode := t.elements[descriptor]; mode { + case inclusionModeExplicit, inclusionModeImplicit, inclusionModeEnclosing: + return true + case inclusionModeExcluded: + return false + case inclusionModeUnknown: + // True as option type is not explicitly excluded. + // Occurs on first traversal when adding included types. + return true + default: + return false } - t.files[file] = struct{}{} - return t.exploreCustomOptions(imageIndex.Files[file], file, imageIndex, opts) } -func (t *transitiveClosure) addPackage( - pkg *protoPackage, +func (t *transitiveClosure) includeType( + typeName protoreflect.FullName, imageIndex *imageIndex, - opts *imageFilterOptions, + options *imageFilterOptions, ) error { - for _, file := range pkg.files { - if err := t.addFile(file.Path(), imageIndex, opts); err != nil { - return err + descriptorInfo, ok := imageIndex.ByName[typeName] + if ok { + // It's a type name + if !options.allowImportedTypes && descriptorInfo.file.IsImport() { + return fmt.Errorf("inclusion of type %q: %w", typeName, ErrImageFilterTypeIsImport) + } + // Check if the type is already excluded. + if mode := t.elements[descriptorInfo.element]; mode == inclusionModeExcluded { + return fmt.Errorf("inclusion of excluded type %q", typeName) } - t.completeFiles[file.Path()] = struct{}{} + // If an extension field, check if the extendee is excluded. + if field, ok := descriptorInfo.element.(*descriptorpb.FieldDescriptorProto); ok && field.Extendee != nil { + extendeeName := protoreflect.FullName(strings.TrimPrefix(field.GetExtendee(), ".")) + extendeeInfo, ok := imageIndex.ByName[extendeeName] + if !ok { + return fmt.Errorf("missing %q", extendeeName) + } + if mode := t.elements[extendeeInfo.element]; mode == inclusionModeExcluded { + return fmt.Errorf("cannot include extension field %q as the extendee type %q is excluded", typeName, extendeeName) + } + } + if err := t.addElement(descriptorInfo.element, "", false, imageIndex, options); err != nil { + return fmt.Errorf("inclusion of type %q: %w", typeName, err) + } + return nil } - for _, descriptor := range pkg.elements { - if err := t.addElement(descriptor, "", false, imageIndex, opts); err != nil { - return err + // It could be a package name + pkg, ok := imageIndex.Packages[string(typeName)] + if !ok { + // but it's not... + return fmt.Errorf("inclusion of type %q: %w", typeName, ErrImageFilterTypeNotFound) + } + if !options.allowImportedTypes { + // if package includes only imported files, then reject + onlyImported := true + for _, file := range pkg.files { + if !file.IsImport() { + onlyImported = false + break + } + } + if onlyImported { + return fmt.Errorf("inclusion of type %q: %w", typeName, ErrImageFilterTypeIsImport) + } + } + for _, file := range pkg.files { + fileDescriptor := file.FileDescriptorProto() + if err := t.addElement(fileDescriptor, "", false, imageIndex, options); err != nil { + return fmt.Errorf("inclusion of type %q: %w", typeName, err) } } return nil } +func (t *transitiveClosure) addImport(fromPath, toPath string) { + if _, ok := t.imports[toPath]; !ok { + t.imports[toPath] = nil // mark as seen + } + if fromPath == "" { + return // the base included type, not imported + } + if fromPath == toPath { + return // no need for a file to import itself + } + imps := t.imports[fromPath] + if imps == nil { + imps = newOrderedImports() + t.imports[fromPath] = imps + } + imps.add(toPath) +} + func (t *transitiveClosure) addElement( descriptor namedDescriptor, referrerFile string, @@ -556,13 +428,7 @@ func (t *transitiveClosure) addElement( opts *imageFilterOptions, ) error { descriptorInfo := imageIndex.ByDescriptor[descriptor] - if err := t.addFile(descriptorInfo.file, imageIndex, opts); err != nil { - return err - } - if referrerFile != "" { - t.addImport(referrerFile, descriptorInfo.file) - } - + t.addImport(referrerFile, descriptorInfo.file.Path()) if existingMode, ok := t.elements[descriptor]; ok && existingMode != inclusionModeEnclosing { if existingMode == inclusionModeImplicit && !impliedByCustomOption { // upgrade from implied to explicitly part of closure @@ -576,69 +442,115 @@ func (t *transitiveClosure) addElement( t.elements[descriptor] = inclusionModeExplicit } - // if this type is enclosed inside another, add enclosing types - if err := t.addEnclosing(descriptorInfo.parent, descriptorInfo.file, imageIndex, opts); err != nil { - return err - } - // add any custom options and their dependencies - if err := t.exploreCustomOptions(descriptor, descriptorInfo.file, imageIndex, opts); err != nil { - return err - } - switch typedDescriptor := descriptor.(type) { + case *descriptorpb.FileDescriptorProto: + typeNames, ok := imageIndex.FileTypes[typedDescriptor.GetName()] + if !ok { + return fmt.Errorf("missing %q", typedDescriptor.GetName()) + } + // A file includes all elements. The types are resolved in the image index + // to ensure all nested types are included. + for _, typeName := range typeNames { + typeInfo := imageIndex.ByName[typeName] + if err := t.addElement(typeInfo.element, "", false, imageIndex, opts); err != nil { + return err + } + } + case *descriptorpb.DescriptorProto: + oneofFieldCounts := make([]int, len(typedDescriptor.GetOneofDecl())) // Options and types for all fields for _, field := range typedDescriptor.GetField() { - if err := t.addFieldType(field, descriptorInfo.file, imageIndex, opts); err != nil { + isIncluded, err := t.addFieldType(field, descriptorInfo.file.Path(), imageIndex, opts) + if err != nil { return err } + if !isIncluded { + continue + } + if index := field.OneofIndex; index != nil { + index := *index + if index < 0 || int(index) >= len(oneofFieldCounts) { + return fmt.Errorf("invalid oneof index %d for field %q", index, field.GetName()) + } + oneofFieldCounts[index]++ + } if err := t.exploreCustomOptions(field, referrerFile, imageIndex, opts); err != nil { return err } } // Options for all oneofs in this message - for _, oneOfDescriptor := range typedDescriptor.GetOneofDecl() { - if err := t.exploreCustomOptions(oneOfDescriptor, descriptorInfo.file, imageIndex, opts); err != nil { + for index, oneOfDescriptor := range typedDescriptor.GetOneofDecl() { + if oneofFieldCounts[index] == 0 { + // An empty oneof is not a valid protobuf construct, so we can + // safely exclude it. + t.elements[oneOfDescriptor] = inclusionModeExcluded + continue + } + if err := t.exploreCustomOptions(oneOfDescriptor, descriptorInfo.file.Path(), imageIndex, opts); err != nil { return err } } // Options for all extension ranges in this message for _, extRange := range typedDescriptor.GetExtensionRange() { - if err := t.exploreCustomOptions(extRange, descriptorInfo.file, imageIndex, opts); err != nil { + if err := t.exploreCustomOptions(extRange, descriptorInfo.file.Path(), imageIndex, opts); err != nil { return err } } case *descriptorpb.EnumDescriptorProto: for _, enumValue := range typedDescriptor.GetValue() { - if err := t.exploreCustomOptions(enumValue, descriptorInfo.file, imageIndex, opts); err != nil { + if err := t.exploreCustomOptions(enumValue, descriptorInfo.file.Path(), imageIndex, opts); err != nil { return err } } case *descriptorpb.ServiceDescriptorProto: for _, method := range typedDescriptor.GetMethod() { + inputName := protoreflect.FullName(strings.TrimPrefix(method.GetInputType(), ".")) + inputInfo, ok := imageIndex.ByName[inputName] + if !ok { + return fmt.Errorf("missing %q", inputName) + } + outputName := protoreflect.FullName(strings.TrimPrefix(method.GetOutputType(), ".")) + outputInfo, ok := imageIndex.ByName[outputName] + if !ok { + return fmt.Errorf("missing %q", outputName) + } + inputMode, outputMode := t.elements[inputInfo.element], t.elements[outputInfo.element] + if inputMode == inclusionModeExcluded || outputMode == inclusionModeExcluded { + // The input or ouptut is excluded, so this method is also excluded. + t.elements[inputInfo.element] = inclusionModeExcluded + continue + } if err := t.addElement(method, "", false, imageIndex, opts); err != nil { return err } } case *descriptorpb.MethodDescriptorProto: - inputName := strings.TrimPrefix(typedDescriptor.GetInputType(), ".") - inputDescriptor, ok := imageIndex.ByName[inputName] + inputName := protoreflect.FullName(strings.TrimPrefix(typedDescriptor.GetInputType(), ".")) + inputInfo, ok := imageIndex.ByName[inputName] if !ok { return fmt.Errorf("missing %q", inputName) } - if err := t.addElement(inputDescriptor, descriptorInfo.file, false, imageIndex, opts); err != nil { - return err - } - - outputName := strings.TrimPrefix(typedDescriptor.GetOutputType(), ".") - outputDescriptor, ok := imageIndex.ByName[outputName] + outputName := protoreflect.FullName(strings.TrimPrefix(typedDescriptor.GetOutputType(), ".")) + outputInfo, ok := imageIndex.ByName[outputName] if !ok { return fmt.Errorf("missing %q", outputName) } - if err := t.addElement(outputDescriptor, descriptorInfo.file, false, imageIndex, opts); err != nil { + if inputMode := t.elements[inputInfo.element]; inputMode == inclusionModeExcluded { + // The input is excluded, it's an error to include the method. + return fmt.Errorf("cannot include method %q as the input type %q is excluded", descriptorInfo.fullName, inputInfo.fullName) + } + if outputMode := t.elements[outputInfo.element]; outputMode == inclusionModeExcluded { + // The output is excluded, it's an error to include the method. + return fmt.Errorf("cannot include method %q as the output type %q is excluded", descriptorInfo.fullName, outputInfo.fullName) + } + if err := t.addElement(inputInfo.element, descriptorInfo.file.Path(), false, imageIndex, opts); err != nil { + return err + } + if err := t.addElement(outputInfo.element, descriptorInfo.file.Path(), false, imageIndex, opts); err != nil { return err } @@ -651,26 +563,145 @@ func (t *transitiveClosure) addElement( if typedDescriptor.GetExtendee() == "" { return fmt.Errorf("expected extendee for field %q to not be empty", descriptorInfo.fullName) } - extendeeName := strings.TrimPrefix(typedDescriptor.GetExtendee(), ".") - extendeeDescriptor, ok := imageIndex.ByName[extendeeName] + extendeeName := protoreflect.FullName(strings.TrimPrefix(typedDescriptor.GetExtendee(), ".")) + extendeeInfo, ok := imageIndex.ByName[extendeeName] if !ok { return fmt.Errorf("missing %q", extendeeName) } - if err := t.addElement(extendeeDescriptor, descriptorInfo.file, impliedByCustomOption, imageIndex, opts); err != nil { + if mode := t.elements[extendeeInfo.element]; mode == inclusionModeExcluded { + // The extendee is excluded, so this extension is also excluded. + t.elements[descriptor] = inclusionModeExcluded + return nil + } + if err := t.addElement(extendeeInfo.element, descriptorInfo.file.Path(), impliedByCustomOption, imageIndex, opts); err != nil { return err } - if err := t.addFieldType(typedDescriptor, descriptorInfo.file, imageIndex, opts); err != nil { + isIncluded, err := t.addFieldType(typedDescriptor, descriptorInfo.file.Path(), imageIndex, opts) + if err != nil { return err } + if !isIncluded { + t.elements[descriptor] = inclusionModeExcluded + return nil + } default: return errorUnsupportedFilterType(descriptor, descriptorInfo.fullName) } + // if this type is enclosed inside another, add enclosing types + if err := t.addEnclosing(descriptorInfo.parent, descriptorInfo.file.Path(), imageIndex, opts); err != nil { + return err + } + // add any custom options and their dependencies + if err := t.exploreCustomOptions(descriptor, descriptorInfo.file.Path(), imageIndex, opts); err != nil { + return err + } + + return nil +} + +func (t *transitiveClosure) excludeType( + typeName protoreflect.FullName, + imageIndex *imageIndex, + options *imageFilterOptions, +) error { + descriptorInfo, ok := imageIndex.ByName[typeName] + if ok { + return t.excludeElement(descriptorInfo.element, imageIndex, options) + } + // It could be a package name + pkg, ok := imageIndex.Packages[string(typeName)] + if !ok { + // but it's not... + return fmt.Errorf("exclusion of type %q: %w", typeName, ErrImageFilterTypeNotFound) + } + // Exclude the package and all of its files. + for _, file := range pkg.files { + fileDescriptor := file.FileDescriptorProto() + if err := t.excludeElement(fileDescriptor, imageIndex, options); err != nil { + return err + } + } return nil } -func errorUnsupportedFilterType(descriptor namedDescriptor, fullName string) error { +func (t *transitiveClosure) excludeElement( + descriptor namedDescriptor, + imageIndex *imageIndex, + opts *imageFilterOptions, +) error { + descriptorInfo := imageIndex.ByDescriptor[descriptor] + if existingMode, ok := t.elements[descriptor]; ok { + if existingMode != inclusionModeExcluded { + return fmt.Errorf("type %q is already included", descriptorInfo.fullName) + } + return nil + } + t.elements[descriptor] = inclusionModeExcluded + switch descriptor := descriptor.(type) { + case *descriptorpb.FileDescriptorProto: + for _, descriptor := range descriptor.GetMessageType() { + if err := t.excludeElement(descriptor, imageIndex, opts); err != nil { + return err + } + } + for _, descriptor := range descriptor.GetEnumType() { + if err := t.excludeElement(descriptor, imageIndex, opts); err != nil { + return err + } + } + for _, descriptor := range descriptor.GetService() { + if err := t.excludeElement(descriptor, imageIndex, opts); err != nil { + return err + } + } + for _, extension := range descriptor.GetExtension() { + if err := t.excludeElement(extension, imageIndex, opts); err != nil { + return err + } + } + case *descriptorpb.DescriptorProto: + // Exclude all sub-elements + for _, descriptor := range descriptor.GetNestedType() { + if err := t.excludeElement(descriptor, imageIndex, opts); err != nil { + return err + } + } + for _, enumDescriptor := range descriptor.GetEnumType() { + if err := t.excludeElement(enumDescriptor, imageIndex, opts); err != nil { + return err + } + } + for _, extensionDescriptor := range descriptor.GetExtension() { + if err := t.excludeElement(extensionDescriptor, imageIndex, opts); err != nil { + return err + } + } + case *descriptorpb.EnumDescriptorProto: + // Enum values are not included in the closure, so nothing to do here. + case *descriptorpb.ServiceDescriptorProto: + for _, descriptor := range descriptor.GetMethod() { + if err := t.excludeElement(descriptor, imageIndex, opts); err != nil { + return err + } + } + case *descriptorpb.MethodDescriptorProto: + case *descriptorpb.FieldDescriptorProto: + // Only extension fields can be excluded. + if descriptor.Extendee == nil { + return errorUnsupportedFilterType(descriptor, descriptorInfo.fullName) + } + if descriptor.GetExtendee() == "" { + return fmt.Errorf("expected extendee for field %q to not be empty", descriptorInfo.fullName) + } + default: + return errorUnsupportedFilterType(descriptor, descriptorInfo.fullName) + } + return nil +} + +func errorUnsupportedFilterType(descriptor namedDescriptor, fullName protoreflect.FullName) error { var descriptorType string switch d := descriptor.(type) { case *descriptorpb.FileDescriptorProto: @@ -705,7 +736,8 @@ func (t *transitiveClosure) addEnclosing(descriptor namedDescriptor, enclosingFi for descriptor != nil { _, isMsg := descriptor.(*descriptorpb.DescriptorProto) _, isSvc := descriptor.(*descriptorpb.ServiceDescriptorProto) - if !isMsg && !isSvc { + _, isFile := descriptor.(*descriptorpb.FileDescriptorProto) + if !isMsg && !isSvc && !isFile { break // not an enclosing type } if _, ok := t.elements[descriptor]; ok { @@ -721,19 +753,23 @@ func (t *transitiveClosure) addEnclosing(descriptor namedDescriptor, enclosingFi return nil } -func (t *transitiveClosure) addFieldType(field *descriptorpb.FieldDescriptorProto, referrerFile string, imageIndex *imageIndex, opts *imageFilterOptions) error { +func (t *transitiveClosure) addFieldType(field *descriptorpb.FieldDescriptorProto, referrerFile string, imageIndex *imageIndex, opts *imageFilterOptions) (bool, error) { switch field.GetType() { case descriptorpb.FieldDescriptorProto_TYPE_ENUM, descriptorpb.FieldDescriptorProto_TYPE_MESSAGE, descriptorpb.FieldDescriptorProto_TYPE_GROUP: - typeName := strings.TrimPrefix(field.GetTypeName(), ".") - typeDescriptor, ok := imageIndex.ByName[typeName] + typeName := protoreflect.FullName(strings.TrimPrefix(field.GetTypeName(), ".")) + info, ok := imageIndex.ByName[typeName] if !ok { - return fmt.Errorf("missing %q", typeName) + return false, fmt.Errorf("missing %q", typeName) } - err := t.addElement(typeDescriptor, referrerFile, false, imageIndex, opts) + if mode := t.elements[info.element]; mode == inclusionModeExcluded { + // The field's type is excluded, so this field is also excluded. + return false, nil + } + err := t.addElement(info.element, referrerFile, false, imageIndex, opts) if err != nil { - return err + return false, err } case descriptorpb.FieldDescriptorProto_TYPE_DOUBLE, descriptorpb.FieldDescriptorProto_TYPE_FLOAT, @@ -752,9 +788,9 @@ func (t *transitiveClosure) addFieldType(field *descriptorpb.FieldDescriptorProt descriptorpb.FieldDescriptorProto_TYPE_SINT64: // nothing to follow, custom options handled below. default: - return fmt.Errorf("unknown field type %d", field.GetType()) + return false, fmt.Errorf("unknown field type %d", field.GetType()) } - return nil + return true, nil } func (t *transitiveClosure) addExtensions( @@ -818,9 +854,12 @@ func (t *transitiveClosure) exploreCustomOptions( return fmt.Errorf("unexpected type for exploring options %T", descriptor) } - optionsName := string(options.Descriptor().FullName()) + optionsName := options.Descriptor().FullName() var err error options.Range(func(fd protoreflect.FieldDescriptor, val protoreflect.Value) bool { + if !t.hasOption(fd, imageIndex, opts) { + return true + } // If the value contains an Any message, we should add the message type // therein to the closure. if err = t.exploreOptionValueForAny(fd, val, referrerFile, imageIndex, opts); err != nil { @@ -834,8 +873,8 @@ func (t *transitiveClosure) exploreCustomOptions( optionsByNumber := imageIndex.NameToOptions[optionsName] field, ok := optionsByNumber[int32(fd.Number())] if !ok { - err = fmt.Errorf("cannot find ext no %d on %s", fd.Number(), optionsName) - return false + // Option is unrecognized, ignore it. + return true } err = t.addElement(field, referrerFile, true, imageIndex, opts) return err == nil @@ -897,8 +936,8 @@ func (t *transitiveClosure) exploreOptionSingularValueForAny( } typeURL := msg.Get(typeURLFd).String() pos := strings.LastIndexByte(typeURL, '/') - msgType := typeURL[pos+1:] - d, _ := imageIndex.ByName[msgType].(*descriptorpb.DescriptorProto) + msgType := protoreflect.FullName(typeURL[pos+1:]) + d, _ := imageIndex.ByName[msgType].element.(*descriptorpb.DescriptorProto) if d != nil { if err := t.addElement(d, referrerFile, false, imageIndex, opts); err != nil { return err @@ -994,6 +1033,9 @@ type imageFilterOptions struct { includeCustomOptions bool includeKnownExtensions bool allowImportedTypes bool + mutateInPlace bool + includeTypes map[string]struct{} + excludeTypes map[string]struct{} } func newImageFilterOptions() *imageFilterOptions { @@ -1005,10 +1047,14 @@ func newImageFilterOptions() *imageFilterOptions { } func stripSourceRetentionOptionsFromFile(imageFile bufimage.ImageFile) (bufimage.ImageFile, error) { - updatedFileDescriptor, err := protopluginutil.StripSourceRetentionOptions(imageFile.FileDescriptorProto()) + fileDescriptor := imageFile.FileDescriptorProto() + updatedFileDescriptor, err := protopluginutil.StripSourceRetentionOptions(fileDescriptor) if err != nil { return nil, err } + if updatedFileDescriptor == fileDescriptor { + return imageFile, nil + } return bufimage.NewImageFile( updatedFileDescriptor, imageFile.FullName(), @@ -1072,3 +1118,8 @@ func (o *orderedImports) keys() []string { } return keys } + +// len returns the number of paths in the ordered imports. +func (o *orderedImports) len() int { + return len(o.pathToIndex) +} diff --git a/private/bufpkg/bufimage/bufimageutil/bufimageutil_test.go b/private/bufpkg/bufimage/bufimageutil/bufimageutil_test.go index 840f139633..6ff80ef10b 100644 --- a/private/bufpkg/bufimage/bufimageutil/bufimageutil_test.go +++ b/private/bufpkg/bufimage/bufimageutil/bufimageutil_test.go @@ -26,6 +26,8 @@ import ( "github.com/bufbuild/buf/private/bufpkg/bufimage" "github.com/bufbuild/buf/private/bufpkg/bufmodule" "github.com/bufbuild/buf/private/bufpkg/bufmodule/bufmoduletesting" + imagev1 "github.com/bufbuild/buf/private/gen/proto/go/buf/alpha/image/v1" + "github.com/bufbuild/buf/private/pkg/app/appext" "github.com/bufbuild/buf/private/pkg/protoencoding" "github.com/bufbuild/buf/private/pkg/slicesext" "github.com/bufbuild/buf/private/pkg/slogtestext" @@ -48,39 +50,86 @@ import ( var shouldUpdateExpectations = os.Getenv("BUFBUILD_BUF_BUFIMAGEUTIL_SHOULD_UPDATE_EXPECTATIONS") -func TestOptions(t *testing.T) { +func TestTypes(t *testing.T) { t.Parallel() t.Run("message", func(t *testing.T) { t.Parallel() - runDiffTest(t, "testdata/options", []string{"pkg.Foo"}, "pkg.Foo.txtar") + runDiffTest(t, "testdata/options", "pkg.Foo.txtar", WithIncludeTypes("pkg.Foo")) }) t.Run("enum", func(t *testing.T) { t.Parallel() - runDiffTest(t, "testdata/options", []string{"pkg.FooEnum"}, "pkg.FooEnum.txtar") + runDiffTest(t, "testdata/options", "pkg.FooEnum.txtar", WithIncludeTypes("pkg.FooEnum")) }) t.Run("service", func(t *testing.T) { t.Parallel() - runDiffTest(t, "testdata/options", []string{"pkg.FooService"}, "pkg.FooService.txtar") + runDiffTest(t, "testdata/options", "pkg.FooService.txtar", WithIncludeTypes("pkg.FooService")) }) t.Run("method", func(t *testing.T) { t.Parallel() - runDiffTest(t, "testdata/options", []string{"pkg.FooService.Do"}, "pkg.FooService.Do.txtar") + runDiffTest(t, "testdata/options", "pkg.FooService.Do.txtar", WithIncludeTypes("pkg.FooService.Do")) }) t.Run("all", func(t *testing.T) { t.Parallel() - runDiffTest(t, "testdata/options", []string{"pkg.Foo", "pkg.FooEnum", "pkg.FooService"}, "all.txtar") + runDiffTest(t, "testdata/options", "all.txtar", WithIncludeTypes("pkg.Foo", "pkg.FooEnum", "pkg.FooService")) }) t.Run("exclude-options", func(t *testing.T) { t.Parallel() - runDiffTest(t, "testdata/options", []string{"pkg.Foo", "pkg.FooEnum", "pkg.FooService"}, "all-exclude-options.txtar", WithExcludeCustomOptions()) + runDiffTest(t, "testdata/options", "all-exclude-options.txtar", WithIncludeTypes("pkg.Foo", "pkg.FooEnum", "pkg.FooService"), WithExcludeCustomOptions()) }) t.Run("files", func(t *testing.T) { t.Parallel() - runDiffTest(t, "testdata/options", []string{"Files"}, "Files.txtar") + runDiffTest(t, "testdata/options", "Files.txtar", WithIncludeTypes("Files")) }) t.Run("all-with-files", func(t *testing.T) { t.Parallel() - runDiffTest(t, "testdata/options", []string{"pkg.Foo", "pkg.FooEnum", "pkg.FooService", "Files"}, "all-with-Files.txtar") + runDiffTest(t, "testdata/options", "all-with-Files.txtar", WithIncludeTypes("pkg.Foo", "pkg.FooEnum", "pkg.FooService", "Files")) + }) + + t.Run("exclude-message", func(t *testing.T) { + t.Parallel() + runDiffTest(t, "testdata/options", "pkg.Foo.exclude.txtar", WithExcludeTypes("pkg.Foo")) + }) + t.Run("exclude-enum", func(t *testing.T) { + t.Parallel() + runDiffTest(t, "testdata/options", "pkg.FooEnum.exclude.txtar", WithExcludeTypes("pkg.FooEnum")) + }) + t.Run("exclude-service", func(t *testing.T) { + t.Parallel() + runDiffTest(t, "testdata/options", "pkg.FooService.exclude.txtar", WithExcludeTypes("pkg.FooService")) + }) + t.Run("exclude-method", func(t *testing.T) { + t.Parallel() + runDiffTest(t, "testdata/options", "pkg.FooService.Do.exclude.txtar", WithExcludeTypes("pkg.FooService.Do")) + }) + t.Run("exclude-all", func(t *testing.T) { + t.Parallel() + runDiffTest(t, "testdata/options", "all.exclude.txtar", WithExcludeTypes("pkg.Foo", "pkg.FooEnum", "pkg.FooService")) + }) + + t.Run("mixed-service-method", func(t *testing.T) { + t.Parallel() + runDiffTest(t, "testdata/options", "pkg.FooService.mixed.txtar", WithIncludeTypes("pkg.FooService"), WithExcludeTypes("pkg.FooService.Do")) + }) + t.Run("include-service-exclude-method-types", func(t *testing.T) { + t.Parallel() + runDiffTest(t, "testdata/options", "pkg.FooService.exclude-method-types.txtar", WithIncludeTypes("pkg.FooService"), WithExcludeTypes("pkg.Empty")) + }) + t.Run("include-method-exclude-method-types", func(t *testing.T) { + t.Parallel() + _, image, err := getImage(context.Background(), slogtestext.NewLogger(t), "testdata/options", bufimage.WithExcludeSourceCodeInfo()) + require.NoError(t, err) + _, err = FilterImage(image, WithIncludeTypes("pkg.FooService", "pkg.FooService.Do"), WithExcludeTypes("pkg.Empty")) + require.Error(t, err) + assert.ErrorContains(t, err, "cannot include method \"pkg.FooService.Do\"") + }) + + t.Run("include-extension-exclude-extendee", func(t *testing.T) { + t.Parallel() + _, image, err := getImage(context.Background(), slogtestext.NewLogger(t), "testdata/options", bufimage.WithExcludeSourceCodeInfo()) + require.NoError(t, err) + _, err = FilterImage(image, WithIncludeTypes("pkg.extension"), WithExcludeTypes("pkg.Foo")) + require.Error(t, err) + assert.ErrorContains(t, err, "cannot include extension field \"pkg.extension\" as the extendee type \"pkg.Foo\" is excluded") }) } @@ -88,19 +137,152 @@ func TestNesting(t *testing.T) { t.Parallel() t.Run("message", func(t *testing.T) { t.Parallel() - runDiffTest(t, "testdata/nesting", []string{"pkg.Foo"}, "message.txtar") + runDiffTest(t, "testdata/nesting", "message.txtar", WithIncludeTypes("pkg.Foo")) }) t.Run("recursenested", func(t *testing.T) { t.Parallel() - runDiffTest(t, "testdata/nesting", []string{"pkg.Foo.NestedFoo.NestedNestedFoo"}, "recursenested.txtar") + runDiffTest(t, "testdata/nesting", "recursenested.txtar", WithIncludeTypes("pkg.Foo.NestedFoo.NestedNestedFoo")) }) t.Run("enum", func(t *testing.T) { t.Parallel() - runDiffTest(t, "testdata/nesting", []string{"pkg.FooEnum"}, "enum.txtar") + runDiffTest(t, "testdata/nesting", "enum.txtar", WithIncludeTypes("pkg.FooEnum")) }) t.Run("usingother", func(t *testing.T) { t.Parallel() - runDiffTest(t, "testdata/nesting", []string{"pkg.Baz"}, "usingother.txtar") + runDiffTest(t, "testdata/nesting", "usingother.txtar", WithIncludeTypes("pkg.Baz")) + }) + + t.Run("exclude_message", func(t *testing.T) { + t.Parallel() + runDiffTest(t, "testdata/nesting", "message.exclude.txtar", WithExcludeTypes("pkg.Foo")) + }) + t.Run("exclude_recursenested", func(t *testing.T) { + t.Parallel() + runDiffTest(t, "testdata/nesting", "recursenested.exclude.txtar", WithExcludeTypes("pkg.Foo.NestedFoo.NestedNestedFoo")) + }) + t.Run("exclude_enum", func(t *testing.T) { + t.Parallel() + runDiffTest(t, "testdata/nesting", "enum.exclude.txtar", WithExcludeTypes("pkg.FooEnum")) + }) + t.Run("exclude_usingother", func(t *testing.T) { + t.Parallel() + runDiffTest(t, "testdata/nesting", "usingother.exclude.txtar", WithExcludeTypes("pkg.Baz")) + }) + + t.Run("mixed", func(t *testing.T) { + t.Parallel() + runDiffTest(t, "testdata/nesting", "mixed.txtar", WithIncludeTypes("pkg.Foo", "pkg.FooEnum"), WithExcludeTypes("pkg.Foo.NestedFoo", "pkg.Baz")) + }) + + t.Run("include-excluded", func(t *testing.T) { + t.Parallel() + ctx := context.Background() + _, image, err := getImage(ctx, slogtestext.NewLogger(t), "testdata/nesting", bufimage.WithExcludeSourceCodeInfo()) + require.NoError(t, err) + _, err = FilterImage(image, WithIncludeTypes("pkg.Foo.NestedFoo"), WithExcludeTypes("pkg.Foo")) + require.ErrorContains(t, err, "inclusion of excluded type \"pkg.Foo.NestedFoo\"") + _, err = FilterImage(image, WithIncludeTypes("pkg.Foo.NestedButNotUsed"), WithExcludeTypes("pkg.Foo")) + require.ErrorContains(t, err, "inclusion of excluded type \"pkg.Foo.NestedButNotUsed\"") + }) +} + +func TestOneof(t *testing.T) { + t.Parallel() + t.Run("exclude-partial", func(t *testing.T) { + t.Parallel() + runDiffTest(t, "testdata/oneofs", "pkg.Foo.exclude-partial.txtar", WithIncludeTypes("pkg.Foo"), WithExcludeTypes("pkg.FooEnum", "pkg.Bar.BarNested")) + }) + t.Run("exclude-bar", func(t *testing.T) { + t.Parallel() + runDiffTest(t, "testdata/oneofs", "pkg.Foo.exclude-bar.txtar", WithIncludeTypes("pkg.Foo"), WithExcludeTypes("pkg.FooEnum", "pkg.Bar")) + }) +} + +func TestOptions(t *testing.T) { + t.Parallel() + t.Run("include_option", func(t *testing.T) { + t.Parallel() + runDiffTest(t, "testdata/options", "options.foo.include.txtar", WithIncludeTypes( + "message_foo", + "field_foo", + "oneof_foo", + "enum_foo", + "enum_value_foo", + "service_foo", + "method_foo", + "UsedOption.file_foo", + )) + }) + t.Run("exclude_foo", func(t *testing.T) { + t.Parallel() + runDiffTest(t, "testdata/options", "options.foo.exclude.txtar", WithExcludeTypes( + "message_foo", + "field_foo", + "oneof_foo", + "enum_foo", + "enum_value_foo", + "service_foo", + "method_foo", + "UsedOption.file_foo", + )) + }) + t.Run("only_file", func(t *testing.T) { + t.Parallel() + runDiffTest(t, "testdata/options", "options.only_file.txtar", WithExcludeTypes( + "message_foo", "message_bar", "message_baz", + "field_foo", "field_bar", "field_baz", + "oneof_foo", "oneof_bar", "oneof_baz", + "enum_foo", "enum_bar", "enum_baz", + "enum_value_foo", "enum_value_bar", "enum_value_baz", + "service_foo", "service_bar", "service_baz", + "method_foo", "method_bar", "method_baz", + )) + }) +} + +func TestOptionImports(t *testing.T) { + t.Parallel() + + // This checks that when excluding options the imports are correctly dropped. + // For this case when both options are removed only a.proto should be left. + testdataDir := "testdata/imports" + bucket, err := storageos.NewProvider().NewReadWriteBucket(testdataDir) + require.NoError(t, err) + testModuleData := []bufmoduletesting.ModuleData{ + { + Bucket: storage.FilterReadBucket(bucket, storage.MatchPathEqual("a.proto")), + }, + { + Bucket: storage.FilterReadBucket(bucket, storage.MatchPathEqual("options.proto")), + NotTargeted: true, + }, + } + moduleSet, err := bufmoduletesting.NewModuleSet(testModuleData...) + require.NoError(t, err) + + // Safe to filter the image concurrently as its not being modified. + image, err := bufimage.BuildImage( + context.Background(), + slogtestext.NewLogger(t), + bufmodule.ModuleSetToModuleReadBucketWithOnlyProtoFiles(moduleSet), + bufimage.WithExcludeSourceCodeInfo(), + ) + require.NoError(t, err) + + t.Run("exclude_foo", func(t *testing.T) { + t.Parallel() + generated := runFilterImage(t, image, WithExcludeTypes("message_foo")) + checkExpectation(t, context.Background(), generated, bucket, "foo.txtar") + }) + t.Run("exclude_foo_bar", func(t *testing.T) { + t.Parallel() + generated := runFilterImage(t, image, WithExcludeTypes("message_foo", "message_bar")) + checkExpectation(t, context.Background(), generated, bucket, "foo_bar.txtar") + }) + t.Run("exclude_bar", func(t *testing.T) { + t.Parallel() + generated := runFilterImage(t, image, WithIncludeTypes("pkg.Foo"), WithExcludeTypes("message_bar")) + checkExpectation(t, context.Background(), generated, bucket, "bar.txtar") }) } @@ -108,44 +290,44 @@ func TestImportModifiers(t *testing.T) { t.Parallel() t.Run("regular_weak", func(t *testing.T) { t.Parallel() - runDiffTest(t, "testdata/importmods", []string{"ImportRegular", "ImportWeak"}, "regular_weak.txtar") + runDiffTest(t, "testdata/importmods", "regular_weak.txtar", WithIncludeTypes("ImportRegular", "ImportWeak")) }) t.Run("weak_public", func(t *testing.T) { t.Parallel() - runDiffTest(t, "testdata/importmods", []string{"ImportWeak", "ImportPublic"}, "weak_public.txtar") + runDiffTest(t, "testdata/importmods", "weak_public.txtar", WithIncludeTypes("ImportWeak", "ImportPublic")) }) t.Run("regular_public", func(t *testing.T) { t.Parallel() - runDiffTest(t, "testdata/importmods", []string{"ImportRegular", "ImportPublic"}, "regular_public.txtar") + runDiffTest(t, "testdata/importmods", "regular_public.txtar", WithIncludeTypes("ImportRegular", "ImportPublic")) }) t.Run("noimports", func(t *testing.T) { t.Parallel() - runDiffTest(t, "testdata/importmods", []string{"NoImports"}, "noimports.txtar") + runDiffTest(t, "testdata/importmods", "noimports.txtar", WithIncludeTypes("NoImports")) }) } func TestExtensions(t *testing.T) { t.Parallel() - runDiffTest(t, "testdata/extensions", []string{"pkg.Foo"}, "extensions.txtar") - runDiffTest(t, "testdata/extensions", []string{"pkg.Foo"}, "extensions-excluded.txtar", WithExcludeKnownExtensions()) + runDiffTest(t, "testdata/extensions", "extensions.txtar", WithIncludeTypes("pkg.Foo")) + runDiffTest(t, "testdata/extensions", "extensions-excluded.txtar", WithExcludeKnownExtensions(), WithIncludeTypes("pkg.Foo")) } func TestPackages(t *testing.T) { t.Parallel() - runDiffTest(t, "testdata/packages", []string{""}, "root.txtar") - runDiffTest(t, "testdata/packages", []string{"foo"}, "foo.txtar") - runDiffTest(t, "testdata/packages", []string{"foo.bar"}, "foo.bar.txtar") - runDiffTest(t, "testdata/packages", []string{"foo.bar.baz"}, "foo.bar.baz.txtar") + runDiffTest(t, "testdata/packages", "root.txtar", WithIncludeTypes("")) + runDiffTest(t, "testdata/packages", "foo.txtar", WithIncludeTypes("foo")) + runDiffTest(t, "testdata/packages", "foo.bar.txtar", WithIncludeTypes("foo.bar")) + runDiffTest(t, "testdata/packages", "foo.bar.baz.txtar", WithIncludeTypes("foo.bar.baz")) } func TestAny(t *testing.T) { t.Parallel() - runDiffTest(t, "testdata/any", []string{"ExtendedAnySyntax"}, "c1.txtar") - runDiffTest(t, "testdata/any", []string{"ExtendedAnySyntax_InField"}, "c2.txtar") - runDiffTest(t, "testdata/any", []string{"ExtendedAnySyntax_InList"}, "c3.txtar") - runDiffTest(t, "testdata/any", []string{"ExtendedAnySyntax_InMap"}, "c4.txtar") - runDiffTest(t, "testdata/any", []string{"NormalMessageSyntaxValidType"}, "d.txtar") - runDiffTest(t, "testdata/any", []string{"NormalMessageSyntaxInvalidType"}, "e.txtar") + runDiffTest(t, "testdata/any", "c1.txtar", WithIncludeTypes("ExtendedAnySyntax")) + runDiffTest(t, "testdata/any", "c2.txtar", WithIncludeTypes("ExtendedAnySyntax_InField")) + runDiffTest(t, "testdata/any", "c3.txtar", WithIncludeTypes("ExtendedAnySyntax_InList")) + runDiffTest(t, "testdata/any", "c4.txtar", WithIncludeTypes("ExtendedAnySyntax_InMap")) + runDiffTest(t, "testdata/any", "d.txtar", WithIncludeTypes("NormalMessageSyntaxValidType")) + runDiffTest(t, "testdata/any", "e.txtar", WithIncludeTypes("NormalMessageSyntaxInvalidType")) } func TestSourceCodeInfo(t *testing.T) { @@ -164,7 +346,8 @@ func TestSourceCodeInfo(t *testing.T) { func TestUnusedDeps(t *testing.T) { t.Parallel() - runDiffTest(t, "testdata/unuseddeps", []string{"a.A"}, "a.txtar") + runDiffTest(t, "testdata/unuseddeps", "a.txtar", WithIncludeTypes("a.A")) + runDiffTest(t, "testdata/unuseddeps", "ab.txtar", WithIncludeTypes("a.A"), WithExcludeTypes("b.B")) } func TestTransitivePublic(t *testing.T) { @@ -186,7 +369,7 @@ func TestTransitivePublic(t *testing.T) { ) require.NoError(t, err) - filteredImage, err := ImageFilteredByTypes(image, "c.Baz") + filteredImage, err := FilterImage(image, WithIncludeTypes("c.Baz")) require.NoError(t, err) _, err = protodesc.NewFiles(bufimage.ImageToFileDescriptorSet(filteredImage)) @@ -226,19 +409,76 @@ func TestTypesFromMainModule(t *testing.T) { bProtoFileInfo, err := dep.StatFileInfo(ctx, "b.proto") require.NoError(t, err) require.False(t, bProtoFileInfo.IsTargetFile()) - _, err = ImageFilteredByTypes(image, "dependency.Dep") + _, err = FilterImage(image, WithIncludeTypes("dependency.Dep")) require.Error(t, err) assert.ErrorIs(t, err, ErrImageFilterTypeIsImport) // allowed if we specify option - _, err = ImageFilteredByTypesWithOptions(image, []string{"dependency.Dep"}, WithAllowFilterByImportedType()) + _, err = FilterImage(image, WithIncludeTypes("dependency.Dep"), WithAllowIncludeOfImportedType()) require.NoError(t, err) - _, err = ImageFilteredByTypes(image, "nonexisting") + _, err = FilterImage(image, WithIncludeTypes("nonexisting")) require.Error(t, err) assert.ErrorIs(t, err, ErrImageFilterTypeNotFound) } +func TestMutateInPlace(t *testing.T) { + t.Parallel() + ctx := context.Background() + _, image, err := getImage(ctx, slogtestext.NewLogger(t), "testdata/options") + require.NoError(t, err) + + aProtoFile := image.GetFile("a.proto") + aFileDescriptorProto := aProtoFile.FileDescriptorProto() + assert.Len(t, aFileDescriptorProto.MessageType, 2) // Foo, Empty + assert.Len(t, aFileDescriptorProto.EnumType, 1) // FooEnum + assert.Len(t, aFileDescriptorProto.Service, 1) + + locationLen := len(aFileDescriptorProto.SourceCodeInfo.Location) + + // Shallow copy + shallowFilteredImage, err := FilterImage(image, WithIncludeTypes("pkg.Foo")) + require.NoError(t, err) + + filteredAFileDescriptorProto := shallowFilteredImage.GetFile("a.proto").FileDescriptorProto() + assert.NotSame(t, aFileDescriptorProto, filteredAFileDescriptorProto) + filterLocationLen := len(filteredAFileDescriptorProto.SourceCodeInfo.Location) + assert.Less(t, filterLocationLen, locationLen) + + // Mutate in place + mutateFilteredImage, err := FilterImage(image, WithIncludeTypes("pkg.Foo"), WithMutateInPlace()) + require.NoError(t, err) + + // Check that the original image was mutated + assert.Same(t, aFileDescriptorProto, mutateFilteredImage.GetFile("a.proto").FileDescriptorProto()) + assert.Len(t, aFileDescriptorProto.MessageType, 1) // Foo + if assert.GreaterOrEqual(t, cap(aFileDescriptorProto.MessageType), 2) { + slice := aFileDescriptorProto.MessageType[1:cap(aFileDescriptorProto.MessageType)] + for _, elem := range slice { + assert.Nil(t, elem) // Empty + } + } + assert.Nil(t, aFileDescriptorProto.EnumType) + assert.Nil(t, aFileDescriptorProto.Service) + assert.Equal(t, filterLocationLen, len(aFileDescriptorProto.SourceCodeInfo.Location)) +} + +func TestConsecutiveFilters(t *testing.T) { + t.Parallel() + + ctx := context.Background() + _, image, err := getImage(ctx, slogtestext.NewLogger(t), "testdata/options") + require.NoError(t, err) + + t.Run("options", func(t *testing.T) { + t.Parallel() + filteredImage, err := FilterImage(image, WithIncludeTypes("pkg.Foo"), WithExcludeTypes("message_baz")) + require.NoError(t, err) + _, err = FilterImage(filteredImage, WithExcludeTypes("message_foo")) + require.NoError(t, err) + }) +} + func getImage(ctx context.Context, logger *slog.Logger, testdataDir string, options ...bufimage.BuildImageOption) (storage.ReadWriteBucket, bufimage.Image, error) { bucket, err := storageos.NewProvider().NewReadWriteBucket(testdataDir) if err != nil { @@ -260,27 +500,32 @@ func getImage(ctx context.Context, logger *slog.Logger, testdataDir string, opti return bucket, image, nil } -func runDiffTest(t *testing.T, testdataDir string, typenames []string, expectedFile string, opts ...ImageFilterOption) { +func runDiffTest(t *testing.T, testdataDir string, expectedFile string, opts ...ImageFilterOption) { ctx := context.Background() bucket, image, err := getImage(ctx, slogtestext.NewLogger(t), testdataDir, bufimage.WithExcludeSourceCodeInfo()) require.NoError(t, err) + generated := runFilterImage(t, image, opts...) + checkExpectation(t, ctx, generated, bucket, expectedFile) +} - filteredImage, err := ImageFilteredByTypesWithOptions(image, typenames, opts...) +func runFilterImage(t *testing.T, image bufimage.Image, opts ...ImageFilterOption) []byte { + filteredImage, err := FilterImage(image, opts...) require.NoError(t, err) - assert.NotNil(t, image) + assert.NotNil(t, filteredImage) assert.True(t, imageIsDependencyOrdered(filteredImage), "image files not in dependency order") // Convert the filtered image back to a proto image and then back to an image to ensure that the // image is still valid after filtering. protoImage, err := bufimage.ImageToProtoImage(filteredImage) require.NoError(t, err) + // Clone here as `bufimage.NewImageForProto` mutates protoImage. + protoImage, _ = proto.Clone(protoImage).(*imagev1.Image) // Safe to assert. filteredImage, err = bufimage.NewImageForProto(protoImage) require.NoError(t, err) // We may have filtered out custom options from the set in the step above. However, the options messages // still contain extension fields that refer to the custom options, as a result of building the image. - // So we serialize and then de-serialize, and use only the filtered results to parse extensions. That - // way, the result will omit custom options that aren't present in the filtered set (as they will be + // So we serialize and then de-serialize, and use only the filtered results to parse extensions. That way, the result will omit custom options that aren't present in the filtered set (as they will be // considered unrecognized fields). fileDescriptorSet := &descriptorpb.FileDescriptorSet{ File: slicesext.Map(filteredImage.Files(), func(imageFile bufimage.ImageFile) *descriptorpb.FileDescriptorProto { @@ -312,7 +557,7 @@ func runDiffTest(t *testing.T, testdataDir string, typenames []string, expectedF return archive.Files[i].Name < archive.Files[j].Name }) generated := txtar.Format(archive) - checkExpectation(t, ctx, generated, bucket, expectedFile) + return generated } func checkExpectation(t *testing.T, ctx context.Context, actual []byte, bucket storage.ReadWriteBucket, expectedFile string) { @@ -336,7 +581,7 @@ func runSourceCodeInfoTest(t *testing.T, typename string, expectedFile string, o bucket, image, err := getImage(ctx, slogtestext.NewLogger(t), "testdata/sourcecodeinfo") require.NoError(t, err) - filteredImage, err := ImageFilteredByTypesWithOptions(image, []string{typename}, opts...) + filteredImage, err := FilterImage(image, append(opts, WithIncludeTypes(typename))...) require.NoError(t, err) imageFile := filteredImage.GetFile("test.proto") @@ -466,7 +711,7 @@ func benchmarkFilterImage(b *testing.B, opts ...bufimage.BuildImageOption) { } ctx := context.Background() for _, benchmarkCase := range benchmarkCases { - _, image, err := getImage(ctx, slogtestext.NewLogger(b), benchmarkCase.folder, opts...) + _, image, err := getImage(ctx, slogtestext.NewLogger(b, slogtestext.WithLogLevel(appext.LogLevelError)), benchmarkCase.folder, opts...) require.NoError(b, err) benchmarkCase.image = image } @@ -490,7 +735,7 @@ func benchmarkFilterImage(b *testing.B, opts ...bufimage.BuildImageOption) { require.NoError(b, err) b.StartTimer() - _, err = ImageFilteredByTypes(image, typeName) + _, err = FilterImage(image, WithIncludeTypes(typeName), WithMutateInPlace()) require.NoError(b, err) i++ if i == b.N { diff --git a/private/bufpkg/bufimage/bufimageutil/image_filter.go b/private/bufpkg/bufimage/bufimageutil/image_filter.go new file mode 100644 index 0000000000..00335ea863 --- /dev/null +++ b/private/bufpkg/bufimage/bufimageutil/image_filter.go @@ -0,0 +1,558 @@ +// Copyright 2020-2025 Buf Technologies, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package bufimageutil + +import ( + "fmt" + "slices" + "strings" + + "github.com/bufbuild/buf/private/bufpkg/bufimage" + "github.com/bufbuild/buf/private/pkg/syserror" + "google.golang.org/protobuf/proto" + "google.golang.org/protobuf/reflect/protoreflect" + "google.golang.org/protobuf/types/descriptorpb" +) + +// filterImage filters the Image for the given options. +func filterImage(image bufimage.Image, options *imageFilterOptions) (bufimage.Image, error) { + imageIndex, err := newImageIndexForImage(image, options) + if err != nil { + return nil, err + } + closure := newTransitiveClosure() + // All excludes are added first, then includes walk included all non excluded types. + // TODO: consider supporting a glob syntax of some kind, to do more advanced pattern + // matching, such as ability to get a package AND all of its sub-packages. + for excludeType := range options.excludeTypes { + excludeType := protoreflect.FullName(excludeType) + if err := closure.excludeType(excludeType, imageIndex, options); err != nil { + return nil, err + } + } + for includeType := range options.includeTypes { + includeType := protoreflect.FullName(includeType) + if err := closure.includeType(includeType, imageIndex, options); err != nil { + return nil, err + } + } + // TODO: No types were included, so include everything. This can be + // removed when we are able to handle finding all required imports + // below, when remapping the descriptor. + if len(options.includeTypes) == 0 { + for _, file := range image.Files() { + if file.IsImport() { + continue + } + if err := closure.addElement(file.FileDescriptorProto(), "", false, imageIndex, options); err != nil { + return nil, err + } + } + } + // After all types are added, add their known extensions + if err := closure.addExtensions(imageIndex, options); err != nil { + return nil, err + } + + // Loop over image files in revserse DAG order. Imports that are no longer + // imported by a previous file are dropped from the image. + dirty := false + newImageFiles := make([]bufimage.ImageFile, 0, len(image.Files())) + for _, imageFile := range slices.Backward(image.Files()) { + imageFilePath := imageFile.Path() + // Check if the file is used. + if _, ok := closure.imports[imageFilePath]; !ok { + continue // Filtered out. + } + newImageFile, err := filterImageFile( + imageFile, + imageIndex, + closure, + options, + ) + if err != nil { + return nil, err + } + dirty = dirty || newImageFile != imageFile + if newImageFile == nil { + return nil, fmt.Errorf("imported file %q was filtered out", imageFilePath) + } + newImageFiles = append(newImageFiles, newImageFile) + } + if !dirty { + return image, nil + } + // Reverse the image files back to DAG order. + slices.Reverse(newImageFiles) + return bufimage.NewImage(newImageFiles) +} + +func filterImageFile( + imageFile bufimage.ImageFile, + imageIndex *imageIndex, + closure *transitiveClosure, + options *imageFilterOptions, +) (bufimage.ImageFile, error) { + fileDescriptor := imageFile.FileDescriptorProto() + var sourcePathsRemap sourcePathsRemapTrie + builder := sourcePathsBuilder{ + imageIndex: imageIndex, + closure: closure, + options: options, + } + newFileDescriptor, changed, err := builder.remapFileDescriptor(&sourcePathsRemap, fileDescriptor) + if err != nil { + return nil, err + } + if newFileDescriptor == nil { + return nil, nil // Filtered out. + } + if !changed { + if len(sourcePathsRemap) > 0 { + return nil, syserror.Newf("unexpected %d sourcePathsRemaps", len(sourcePathsRemap)) + } + return imageFile, nil // No changes required. + } + + // Remap the source code info. + if locations := fileDescriptor.SourceCodeInfo.GetLocation(); len(locations) > 0 { + newLocations := make([]*descriptorpb.SourceCodeInfo_Location, 0, len(locations)) + for _, location := range locations { + oldPath := location.Path + newPath, noComment := sourcePathsRemap.newPath(oldPath) + if newPath == nil { + continue + } + if noComment || !slices.Equal(oldPath, newPath) { + location = maybeClone(location, options) + location.Path = newPath + } + if noComment { + location.LeadingDetachedComments = nil + location.LeadingComments = nil + location.TrailingComments = nil + } + newLocations = append(newLocations, location) + } + newFileDescriptor.SourceCodeInfo = &descriptorpb.SourceCodeInfo{ + Location: newLocations, + } + } + return bufimage.NewImageFile( + newFileDescriptor, + imageFile.FullName(), + imageFile.CommitID(), + imageFile.ExternalPath(), + imageFile.LocalPath(), + imageFile.IsImport(), + imageFile.IsSyntaxUnspecified(), + nil, // There are no unused dependencies. + ) +} + +// sourcePathsBuilder is a helper for building the new source paths. +// Each method return the new value, whether it was changed, and an error if any. +// The value is nil if it was filtered out. +type sourcePathsBuilder struct { + imageIndex *imageIndex + closure *transitiveClosure + options *imageFilterOptions +} + +func (b *sourcePathsBuilder) remapFileDescriptor( + sourcePathsRemap *sourcePathsRemapTrie, + fileDescriptor *descriptorpb.FileDescriptorProto, +) (*descriptorpb.FileDescriptorProto, bool, error) { + if !b.closure.hasType(fileDescriptor, b.options) { + return nil, true, nil + } + + sourcePath := make(protoreflect.SourcePath, 0, 8) + + // Walk the file descriptor. + isDirty := false + newMessages, changed, err := remapSlice(sourcePathsRemap, append(sourcePath, fileMessagesTag), fileDescriptor.MessageType, b.remapDescriptor, b.options) + if err != nil { + return nil, false, err + } + isDirty = isDirty || changed + newEnums, changed, err := remapSlice(sourcePathsRemap, append(sourcePath, fileEnumsTag), fileDescriptor.EnumType, b.remapEnum, b.options) + if err != nil { + return nil, false, err + } + isDirty = isDirty || changed + newServices, changed, err := remapSlice(sourcePathsRemap, append(sourcePath, fileServicesTag), fileDescriptor.Service, b.remapService, b.options) + if err != nil { + return nil, false, err + } + isDirty = isDirty || changed + newExtensions, changed, err := remapSlice(sourcePathsRemap, append(sourcePath, fileExtensionsTag), fileDescriptor.Extension, b.remapField, b.options) + if err != nil { + return nil, false, err + } + isDirty = isDirty || changed + newDependencies, newPublicDependencies, newWeakDependencies, changed, err := b.remapDependencies(sourcePathsRemap, sourcePath, fileDescriptor) + if err != nil { + return nil, false, err + } + isDirty = isDirty || changed + if !isDirty { + return fileDescriptor, false, nil + } + + newFileDescriptor := maybeClone(fileDescriptor, b.options) + newFileDescriptor.MessageType = newMessages + newFileDescriptor.EnumType = newEnums + newFileDescriptor.Service = newServices + newFileDescriptor.Extension = newExtensions + newFileDescriptor.Dependency = newDependencies + newFileDescriptor.PublicDependency = newPublicDependencies + newFileDescriptor.WeakDependency = newWeakDependencies + return newFileDescriptor, true, nil +} + +func (b *sourcePathsBuilder) remapDependencies( + sourcePathsRemap *sourcePathsRemapTrie, + sourcePath protoreflect.SourcePath, + fileDescriptor *descriptorpb.FileDescriptorProto, +) ([]string, []int32, []int32, bool, error) { + dependencies := fileDescriptor.GetDependency() + publicDependencies := fileDescriptor.GetPublicDependency() + weakDependencies := fileDescriptor.GetWeakDependency() + + // Check if the imports need to be remapped. + importsRequired := b.closure.imports[fileDescriptor.GetName()] + importsCount := 0 + if importsRequired != nil { + importsCount = importsRequired.len() + } + for _, importPath := range dependencies { + if importsRequired != nil && importsRequired.index(importPath) != -1 { + importsCount-- + } else { + importsCount = -1 + break + } + } + if importsCount == 0 && len(publicDependencies) == 0 { + // Imports match and no public dependencies. + return dependencies, publicDependencies, weakDependencies, false, nil + } + + indexFrom, indexTo := int32(0), int32(0) + var newDependencies []string + if b.options.mutateInPlace { + newDependencies = dependencies[:0] + } + dependencyPath := append(sourcePath, fileDependencyTag) + dependencyChanges := make([]int32, len(dependencies)) + for _, importPath := range dependencies { + path := append(dependencyPath, indexFrom) + if importsRequired != nil && importsRequired.index(importPath) != -1 { + dependencyChanges[indexFrom] = indexTo + if indexTo != indexFrom { + sourcePathsRemap.markMoved(path, indexTo) + } + newDependencies = append(newDependencies, importPath) + indexTo++ + // delete them as we go, so we know which ones weren't in the list + importsRequired.delete(importPath) + } else { + sourcePathsRemap.markDeleted(path) + dependencyChanges[indexFrom] = -1 + } + indexFrom++ + } + if importsRequired != nil { + newDependencies = append(newDependencies, importsRequired.keys()...) + } + + // Pulbic dependencies are always removed on remapping. + publicDependencyPath := append(sourcePath, filePublicDependencyTag) + sourcePathsRemap.markDeleted(publicDependencyPath) + + var newWeakDependencies []int32 + if len(weakDependencies) > 0 { + if b.options.mutateInPlace { + newWeakDependencies = weakDependencies[:0] + } + weakDependencyPath := append(sourcePath, fileWeakDependencyTag) + for _, indexFrom := range weakDependencies { + path := append(weakDependencyPath, indexFrom) + indexTo := dependencyChanges[indexFrom] + if indexTo == -1 { + sourcePathsRemap.markDeleted(path) + } else { + if indexTo != indexFrom { + sourcePathsRemap.markMoved(path, indexTo) + } + newWeakDependencies = append(newWeakDependencies, indexTo) + } + } + } + return newDependencies, nil, newWeakDependencies, true, nil +} + +func (b *sourcePathsBuilder) remapDescriptor( + sourcePathsRemap *sourcePathsRemapTrie, + sourcePath protoreflect.SourcePath, + descriptor *descriptorpb.DescriptorProto, +) (*descriptorpb.DescriptorProto, bool, error) { + if !b.closure.hasType(descriptor, b.options) { + return nil, true, nil + } + var newDescriptor *descriptorpb.DescriptorProto + isDirty := false + if mode := b.closure.elements[descriptor]; mode == inclusionModeEnclosing { + // If the type is only enclosing, only the namespace matters. + if len(descriptor.Field) > 0 || + len(descriptor.OneofDecl) > 0 || + len(descriptor.ExtensionRange) > 0 || + len(descriptor.ReservedRange) > 0 || + len(descriptor.ReservedName) > 0 { + // Clear unnecessary fields. + isDirty = true + newDescriptor = maybeClone(descriptor, b.options) + sourcePathsRemap.markNoComment(sourcePath) + sourcePathsRemap.markDeleted(append(sourcePath, messageFieldsTag)) + sourcePathsRemap.markDeleted(append(sourcePath, messageOneofsTag)) + sourcePathsRemap.markDeleted(append(sourcePath, messageExtensionRangesTag)) + sourcePathsRemap.markDeleted(append(sourcePath, messageReservedRangesTag)) + sourcePathsRemap.markDeleted(append(sourcePath, messageReservedNamesTag)) + newDescriptor.Field = nil + newDescriptor.OneofDecl = nil + newDescriptor.ExtensionRange = nil + newDescriptor.ReservedRange = nil + newDescriptor.ReservedName = nil + } + } else { + newFields, changed, err := remapSlice(sourcePathsRemap, append(sourcePath, messageFieldsTag), descriptor.GetField(), b.remapField, b.options) + if err != nil { + return nil, false, err + } + isDirty = isDirty || changed + newOneofs, changed, err := remapSlice(sourcePathsRemap, append(sourcePath, messageOneofsTag), descriptor.OneofDecl, b.remapOneof, b.options) + if err != nil { + return nil, false, err + } + isDirty = isDirty || changed + if isDirty { + newDescriptor = maybeClone(descriptor, b.options) + newDescriptor.Field = newFields + newDescriptor.OneofDecl = newOneofs + } + } + newExtensions, changed, err := remapSlice(sourcePathsRemap, append(sourcePath, messageExtensionsTag), descriptor.GetExtension(), b.remapField, b.options) + if err != nil { + return nil, false, err + } + isDirty = isDirty || changed + newDescriptors, changed, err := remapSlice(sourcePathsRemap, append(sourcePath, messageNestedMessagesTag), descriptor.NestedType, b.remapDescriptor, b.options) + if err != nil { + return nil, false, err + } + isDirty = isDirty || changed + newEnums, changed, err := remapSlice(sourcePathsRemap, append(sourcePath, messageEnumsTag), descriptor.EnumType, b.remapEnum, b.options) + if err != nil { + return nil, false, err + } + isDirty = isDirty || changed + + if !isDirty { + return descriptor, false, nil + } + if newDescriptor == nil { + newDescriptor = maybeClone(descriptor, b.options) + } + newDescriptor.Extension = newExtensions + newDescriptor.NestedType = newDescriptors + newDescriptor.EnumType = newEnums + return newDescriptor, true, nil +} + +func (b *sourcePathsBuilder) remapEnum( + sourcePathsRemap *sourcePathsRemapTrie, + sourcePath protoreflect.SourcePath, + enum *descriptorpb.EnumDescriptorProto, +) (*descriptorpb.EnumDescriptorProto, bool, error) { + if !b.closure.hasType(enum, b.options) { + // The type is excluded, enum values cannot be excluded individually. + return nil, true, nil + } + return enum, false, nil +} + +func (b *sourcePathsBuilder) remapOneof( + sourcePathsRemap *sourcePathsRemapTrie, + sourcePath protoreflect.SourcePath, + oneof *descriptorpb.OneofDescriptorProto, +) (*descriptorpb.OneofDescriptorProto, bool, error) { + if mode, ok := b.closure.elements[oneof]; ok && mode == inclusionModeExcluded { + // Oneofs are implicitly excluded when all of its fields types are excluded. + return nil, true, nil + } + return oneof, false, nil +} + +func (b *sourcePathsBuilder) remapService( + sourcePathsRemap *sourcePathsRemapTrie, + sourcePath protoreflect.SourcePath, + service *descriptorpb.ServiceDescriptorProto, +) (*descriptorpb.ServiceDescriptorProto, bool, error) { + if !b.closure.hasType(service, b.options) { + return nil, true, nil + } + isDirty := false + // Walk the service methods. + newMethods, changed, err := remapSlice(sourcePathsRemap, append(sourcePath, serviceMethodsTag), service.Method, b.remapMethod, b.options) + if err != nil { + return nil, false, err + } + isDirty = isDirty || changed + if !isDirty { + return service, false, nil + } + newService := maybeClone(service, b.options) + newService.Method = newMethods + return newService, true, nil +} + +func (b *sourcePathsBuilder) remapMethod( + sourcePathsRemap *sourcePathsRemapTrie, + sourcePath protoreflect.SourcePath, + method *descriptorpb.MethodDescriptorProto, +) (*descriptorpb.MethodDescriptorProto, bool, error) { + if !b.closure.hasType(method, b.options) { + return nil, true, nil + } + return method, false, nil +} + +func (b *sourcePathsBuilder) remapField( + sourcePathsRemap *sourcePathsRemapTrie, + sourcePath protoreflect.SourcePath, + field *descriptorpb.FieldDescriptorProto, +) (*descriptorpb.FieldDescriptorProto, bool, error) { + if field.Extendee != nil { + // Extensions are filtered by type. + if !b.closure.hasType(field, b.options) { + return nil, true, nil + } + } + switch field.GetType() { + case descriptorpb.FieldDescriptorProto_TYPE_ENUM, + descriptorpb.FieldDescriptorProto_TYPE_MESSAGE, + descriptorpb.FieldDescriptorProto_TYPE_GROUP: + typeName := protoreflect.FullName(strings.TrimPrefix(field.GetTypeName(), ".")) + typeInfo := b.imageIndex.ByName[typeName] + if !b.closure.hasType(typeInfo.element, b.options) { + return nil, true, nil + } + case descriptorpb.FieldDescriptorProto_TYPE_DOUBLE, + descriptorpb.FieldDescriptorProto_TYPE_FLOAT, + descriptorpb.FieldDescriptorProto_TYPE_INT64, + descriptorpb.FieldDescriptorProto_TYPE_UINT64, + descriptorpb.FieldDescriptorProto_TYPE_INT32, + descriptorpb.FieldDescriptorProto_TYPE_FIXED64, + descriptorpb.FieldDescriptorProto_TYPE_FIXED32, + descriptorpb.FieldDescriptorProto_TYPE_BOOL, + descriptorpb.FieldDescriptorProto_TYPE_STRING, + descriptorpb.FieldDescriptorProto_TYPE_BYTES, + descriptorpb.FieldDescriptorProto_TYPE_UINT32, + descriptorpb.FieldDescriptorProto_TYPE_SFIXED32, + descriptorpb.FieldDescriptorProto_TYPE_SFIXED64, + descriptorpb.FieldDescriptorProto_TYPE_SINT32, + descriptorpb.FieldDescriptorProto_TYPE_SINT64: + default: + return nil, false, fmt.Errorf("unknown field type %d", field.GetType()) + } + return field, false, nil +} + +func remapSlice[T any]( + sourcePathsRemap *sourcePathsRemapTrie, + sourcePath protoreflect.SourcePath, + list []*T, + remapItem func(*sourcePathsRemapTrie, protoreflect.SourcePath, *T) (*T, bool, error), + options *imageFilterOptions, +) ([]*T, bool, error) { + isDirty := false + var newList []*T + fromIndex, toIndex := int32(0), int32(0) + for int(fromIndex) < len(list) { + item := list[fromIndex] + sourcePath := append(sourcePath, fromIndex) + newItem, changed, err := remapItem(sourcePathsRemap, sourcePath, item) + if err != nil { + return nil, false, err + } + isDirty = isDirty || changed + if isDirty && newList == nil { + if options.mutateInPlace { + newList = list[:toIndex] + } else { + newList = append(newList, list[:toIndex]...) + } + } + isIncluded := newItem != nil + if isIncluded { + if fromIndex != toIndex { + sourcePathsRemap.markMoved(sourcePath, toIndex) + } + if isDirty { + newList = append(newList, newItem) + } + toIndex++ + } else { + sourcePathsRemap.markDeleted(sourcePath) + } + fromIndex++ + } + if toIndex == 0 { + isDirty = true + sourcePathsRemap.markDeleted(sourcePath) + } + if isDirty { + if len(newList) == 0 { + return nil, true, nil + } + if options.mutateInPlace { + // Zero out the remaining elements. + for i := int(toIndex); i < len(list); i++ { + list[i] = nil + } + } + return newList, true, nil + } + return list, false, nil +} + +func maybeClone[T proto.Message](value T, options *imageFilterOptions) T { + if !options.mutateInPlace { + return shallowClone(value) + } + return value +} + +func shallowClone[T proto.Message](message T) T { + src := message.ProtoReflect() + dst := src.New() + src.Range(func(fd protoreflect.FieldDescriptor, v protoreflect.Value) bool { + dst.Set(fd, v) + return true + }) + value, _ := dst.Interface().(T) // Safe to assert. + return value +} diff --git a/private/bufpkg/bufimage/bufimageutil/image_index.go b/private/bufpkg/bufimage/bufimageutil/image_index.go index 7840c50f27..7100183e8b 100644 --- a/private/bufpkg/bufimage/bufimageutil/image_index.go +++ b/private/bufpkg/bufimage/bufimageutil/image_index.go @@ -34,21 +34,18 @@ type imageIndex struct { ByDescriptor map[namedDescriptor]elementInfo // ByName maps fully qualified type names to information about the named // element. - ByName map[string]namedDescriptor - // Files maps fully qualified type names to the path of the file that - // declares the type. - Files map[string]*descriptorpb.FileDescriptorProto - + ByName map[protoreflect.FullName]elementInfo // NameToExtensions maps fully qualified type names to all known // extension definitions for a type name. - NameToExtensions map[string][]*descriptorpb.FieldDescriptorProto - + NameToExtensions map[protoreflect.FullName][]*descriptorpb.FieldDescriptorProto // NameToOptions maps `google.protobuf.*Options` type names to their // known extensions by field tag. - NameToOptions map[string]map[int32]*descriptorpb.FieldDescriptorProto - + NameToOptions map[protoreflect.FullName]map[int32]*descriptorpb.FieldDescriptorProto // Packages maps package names to package contents. - Packages map[string]*protoPackage + Packages map[string]*packageInfo + // FileTypes maps file names to the fully qualified type names defined + // in the file. + FileTypes map[string][]protoreflect.FullName } type namedDescriptor interface { @@ -66,29 +63,31 @@ var _ namedDescriptor = (*descriptorpb.ServiceDescriptorProto)(nil) var _ namedDescriptor = (*descriptorpb.MethodDescriptorProto)(nil) type elementInfo struct { - fullName, file string - parent namedDescriptor + fullName protoreflect.FullName + file bufimage.ImageFile + parent namedDescriptor + element namedDescriptor } -type protoPackage struct { +type packageInfo struct { + fullName protoreflect.FullName files []bufimage.ImageFile - elements []namedDescriptor - subPackages []*protoPackage + subPackages []*packageInfo } // newImageIndexForImage builds an imageIndex for a given image. -func newImageIndexForImage(image bufimage.Image, opts *imageFilterOptions) (*imageIndex, error) { +func newImageIndexForImage(image bufimage.Image, options *imageFilterOptions) (*imageIndex, error) { index := &imageIndex{ - ByName: make(map[string]namedDescriptor), + ByName: make(map[protoreflect.FullName]elementInfo), ByDescriptor: make(map[namedDescriptor]elementInfo), - Files: make(map[string]*descriptorpb.FileDescriptorProto), - Packages: make(map[string]*protoPackage), + Packages: make(map[string]*packageInfo), + FileTypes: make(map[string][]protoreflect.FullName), } - if opts.includeCustomOptions { - index.NameToOptions = make(map[string]map[int32]*descriptorpb.FieldDescriptorProto) + if options.includeCustomOptions { + index.NameToOptions = make(map[protoreflect.FullName]map[int32]*descriptorpb.FieldDescriptorProto) } - if opts.includeKnownExtensions { - index.NameToExtensions = make(map[string][]*descriptorpb.FieldDescriptorProto) + if options.includeKnownExtensions { + index.NameToExtensions = make(map[protoreflect.FullName][]*descriptorpb.FieldDescriptorProto) } for _, imageFile := range image.Files() { @@ -96,18 +95,23 @@ func newImageIndexForImage(image bufimage.Image, opts *imageFilterOptions) (*ima pkg.files = append(pkg.files, imageFile) fileName := imageFile.Path() fileDescriptorProto := imageFile.FileDescriptorProto() - index.Files[fileName] = fileDescriptorProto + index.ByDescriptor[fileDescriptorProto] = elementInfo{ + fullName: pkg.fullName, + file: imageFile, + element: fileDescriptorProto, + parent: nil, + } err := walk.DescriptorProtos(fileDescriptorProto, func(name protoreflect.FullName, msg proto.Message) error { - if existing := index.ByName[string(name)]; existing != nil { + if _, existing := index.ByName[name]; existing { return fmt.Errorf("duplicate for %q", name) } descriptor, ok := msg.(namedDescriptor) if !ok { return fmt.Errorf("unexpected descriptor type %T", msg) } - var parent namedDescriptor + var parent namedDescriptor = fileDescriptorProto if pos := strings.LastIndexByte(string(name), '.'); pos != -1 { - parent = index.ByName[string(name[:pos])] + parent = index.ByName[name[:pos]].element if parent == nil { // parent name was a package name, not an element name parent = fileDescriptorProto @@ -116,43 +120,36 @@ func newImageIndexForImage(image bufimage.Image, opts *imageFilterOptions) (*ima // certain descriptor types don't need to be indexed: // enum values, normal (non-extension) fields, and oneofs - var includeInIndex bool switch d := descriptor.(type) { case *descriptorpb.EnumValueDescriptorProto, *descriptorpb.OneofDescriptorProto: // do not add to package elements; these elements are implicitly included by their enclosing type + return nil case *descriptorpb.FieldDescriptorProto: // only add to elements if an extension (regular fields implicitly included by containing message) - includeInIndex = d.Extendee != nil - default: - includeInIndex = true - } - - if includeInIndex { - index.ByName[string(name)] = descriptor - index.ByDescriptor[descriptor] = elementInfo{ - fullName: string(name), - parent: parent, - file: fileName, + if d.Extendee == nil { + return nil } - pkg.elements = append(pkg.elements, descriptor) - } - - ext, ok := descriptor.(*descriptorpb.FieldDescriptorProto) - if !ok || ext.Extendee == nil { - // not an extension, so the rest does not apply - return nil - } - - extendeeName := strings.TrimPrefix(ext.GetExtendee(), ".") - if opts.includeCustomOptions && isOptionsTypeName(extendeeName) { - if _, ok := index.NameToOptions[extendeeName]; !ok { - index.NameToOptions[extendeeName] = make(map[int32]*descriptorpb.FieldDescriptorProto) + extendeeName := protoreflect.FullName(strings.TrimPrefix(d.GetExtendee(), ".")) + if options.includeCustomOptions && isOptionsTypeName(extendeeName) { + if _, ok := index.NameToOptions[extendeeName]; !ok { + index.NameToOptions[extendeeName] = make(map[int32]*descriptorpb.FieldDescriptorProto) + } + index.NameToOptions[extendeeName][d.GetNumber()] = d + } + if options.includeKnownExtensions { + index.NameToExtensions[extendeeName] = append(index.NameToExtensions[extendeeName], d) } - index.NameToOptions[extendeeName][ext.GetNumber()] = ext } - if opts.includeKnownExtensions { - index.NameToExtensions[extendeeName] = append(index.NameToExtensions[extendeeName], ext) + + info := elementInfo{ + fullName: name, + file: imageFile, + parent: parent, + element: descriptor, } + index.ByName[name] = info + index.ByDescriptor[descriptor] = info + index.FileTypes[fileName] = append(index.FileTypes[fileName], name) return nil }) @@ -163,12 +160,14 @@ func newImageIndexForImage(image bufimage.Image, opts *imageFilterOptions) (*ima return index, nil } -func addPackageToIndex(pkgName string, index *imageIndex) *protoPackage { +func addPackageToIndex(pkgName string, index *imageIndex) *packageInfo { pkg := index.Packages[pkgName] if pkg != nil { return pkg } - pkg = &protoPackage{} + pkg = &packageInfo{ + fullName: protoreflect.FullName(pkgName), + } index.Packages[pkgName] = pkg if pkgName == "" { return pkg @@ -182,7 +181,7 @@ func addPackageToIndex(pkgName string, index *imageIndex) *protoPackage { return pkg } -func isOptionsTypeName(typeName string) bool { +func isOptionsTypeName(typeName protoreflect.FullName) bool { switch typeName { case "google.protobuf.FileOptions", "google.protobuf.MessageOptions", diff --git a/private/bufpkg/bufimage/bufimageutil/tags.go b/private/bufpkg/bufimage/bufimageutil/tags.go index 66d91c31c6..cf698a6f7b 100644 --- a/private/bufpkg/bufimage/bufimageutil/tags.go +++ b/private/bufpkg/bufimage/bufimageutil/tags.go @@ -26,14 +26,23 @@ const ( fileEnumsTag = 5 fileServicesTag = 6 fileExtensionsTag = 7 + fileOptionsTag = 8 messageFieldsTag = 2 messageNestedMessagesTag = 3 messageEnumsTag = 4 messageExtensionsTag = 6 + messageOptionsTag = 7 messageOneofsTag = 8 messageExtensionRangesTag = 5 messageReservedRangesTag = 9 messageReservedNamesTag = 10 + extensionRangeOptionsTag = 3 + fieldOptionsTag = 8 + oneofOptionsTag = 2 + enumOptionsTag = 3 enumValuesTag = 2 + enumValueOptionsTag = 3 serviceMethodsTag = 2 + serviceOptionsTag = 3 + methodOptionsTag = 4 ) diff --git a/private/bufpkg/bufimage/bufimageutil/testdata/imports/a.proto b/private/bufpkg/bufimage/bufimageutil/testdata/imports/a.proto new file mode 100644 index 0000000000..4de73ce641 --- /dev/null +++ b/private/bufpkg/bufimage/bufimageutil/testdata/imports/a.proto @@ -0,0 +1,12 @@ +syntax = "proto3"; +package pkg; +import "options.proto"; + +message Foo { + option (message_foo).foo = "str"; + message Bar { + option (message_bar).bar = "str"; + string bar = 1; + } + Bar nested_bar = 2; +} diff --git a/private/bufpkg/bufimage/bufimageutil/testdata/imports/bar.txtar b/private/bufpkg/bufimage/bufimageutil/testdata/imports/bar.txtar new file mode 100644 index 0000000000..ed614ef216 --- /dev/null +++ b/private/bufpkg/bufimage/bufimageutil/testdata/imports/bar.txtar @@ -0,0 +1,161 @@ +-- a.proto -- +syntax = "proto3"; +package pkg; +import "options.proto"; +message Foo { + option (message_foo) = { foo: "str" }; + Bar nested_bar = 2; + message Bar { + string bar = 1; + } +} +-- google/protobuf/descriptor.proto -- +syntax = "proto2"; +package google.protobuf; +option cc_enable_arenas = true; +option csharp_namespace = "Google.Protobuf.Reflection"; +option go_package = "google.golang.org/protobuf/types/descriptorpb"; +option java_outer_classname = "DescriptorProtos"; +option java_package = "com.google.protobuf"; +option objc_class_prefix = "GPB"; +option optimize_for = SPEED; +message FeatureSet { + optional FieldPresence field_presence = 1 [ + edition_defaults = { value: "EXPLICIT", edition: EDITION_LEGACY }, + edition_defaults = { value: "IMPLICIT", edition: EDITION_PROTO3 }, + edition_defaults = { value: "EXPLICIT", edition: EDITION_2023 }, + feature_support = { edition_introduced: EDITION_2023 }, + retention = RETENTION_RUNTIME, + targets = TARGET_TYPE_FIELD, + targets = TARGET_TYPE_FILE + ]; + optional EnumType enum_type = 2 [ + edition_defaults = { value: "CLOSED", edition: EDITION_LEGACY }, + edition_defaults = { value: "OPEN", edition: EDITION_PROTO3 }, + feature_support = { edition_introduced: EDITION_2023 }, + retention = RETENTION_RUNTIME, + targets = TARGET_TYPE_ENUM, + targets = TARGET_TYPE_FILE + ]; + optional RepeatedFieldEncoding repeated_field_encoding = 3 [ + edition_defaults = { value: "EXPANDED", edition: EDITION_LEGACY }, + edition_defaults = { value: "PACKED", edition: EDITION_PROTO3 }, + feature_support = { edition_introduced: EDITION_2023 }, + retention = RETENTION_RUNTIME, + targets = TARGET_TYPE_FIELD, + targets = TARGET_TYPE_FILE + ]; + optional Utf8Validation utf8_validation = 4 [ + edition_defaults = { value: "NONE", edition: EDITION_LEGACY }, + edition_defaults = { value: "VERIFY", edition: EDITION_PROTO3 }, + feature_support = { edition_introduced: EDITION_2023 }, + retention = RETENTION_RUNTIME, + targets = TARGET_TYPE_FIELD, + targets = TARGET_TYPE_FILE + ]; + optional MessageEncoding message_encoding = 5 [ + edition_defaults = { value: "LENGTH_PREFIXED", edition: EDITION_LEGACY }, + feature_support = { edition_introduced: EDITION_2023 }, + retention = RETENTION_RUNTIME, + targets = TARGET_TYPE_FIELD, + targets = TARGET_TYPE_FILE + ]; + optional JsonFormat json_format = 6 [ + edition_defaults = { value: "LEGACY_BEST_EFFORT", edition: EDITION_LEGACY }, + edition_defaults = { value: "ALLOW", edition: EDITION_PROTO3 }, + feature_support = { edition_introduced: EDITION_2023 }, + retention = RETENTION_RUNTIME, + targets = TARGET_TYPE_MESSAGE, + targets = TARGET_TYPE_ENUM, + targets = TARGET_TYPE_FILE + ]; + enum EnumType { + ENUM_TYPE_UNKNOWN = 0; + OPEN = 1; + CLOSED = 2; + } + enum FieldPresence { + FIELD_PRESENCE_UNKNOWN = 0; + EXPLICIT = 1; + IMPLICIT = 2; + LEGACY_REQUIRED = 3; + } + enum JsonFormat { + JSON_FORMAT_UNKNOWN = 0; + ALLOW = 1; + LEGACY_BEST_EFFORT = 2; + } + enum MessageEncoding { + MESSAGE_ENCODING_UNKNOWN = 0; + LENGTH_PREFIXED = 1; + DELIMITED = 2; + } + enum RepeatedFieldEncoding { + REPEATED_FIELD_ENCODING_UNKNOWN = 0; + PACKED = 1; + EXPANDED = 2; + } + enum Utf8Validation { + UTF8_VALIDATION_UNKNOWN = 0; + VERIFY = 2; + NONE = 3; + reserved 1; + } + extensions 1000 to 9994 [ + declaration = { + number: 1000, + full_name: ".pb.cpp", + type: ".pb.CppFeatures" + }, + declaration = { + number: 1001, + full_name: ".pb.java", + type: ".pb.JavaFeatures" + }, + declaration = { + number: 1002, + full_name: ".pb.go", + type: ".pb.GoFeatures" + }, + declaration = { + number: 9990, + full_name: ".pb.proto1", + type: ".pb.Proto1Features" + } + ]; + extensions 9995 to 9999, 10000; + reserved 999; +} +message MessageOptions { + optional bool message_set_wire_format = 1 [default = false]; + optional bool no_standard_descriptor_accessor = 2 [default = false]; + optional bool deprecated = 3 [default = false]; + optional bool map_entry = 7; + optional bool deprecated_legacy_json_field_conflicts = 11 [deprecated = true]; + optional FeatureSet features = 12; + repeated UninterpretedOption uninterpreted_option = 999; + extensions 1000 to max; + reserved 4, 5, 6, 8, 9; +} +message UninterpretedOption { + repeated NamePart name = 2; + optional string identifier_value = 3; + optional uint64 positive_int_value = 4; + optional int64 negative_int_value = 5; + optional double double_value = 6; + optional bytes string_value = 7; + optional string aggregate_value = 8; + message NamePart { + required string name_part = 1; + required bool is_extension = 2; + } +} +-- options.proto -- +syntax = "proto3"; +import "google/protobuf/descriptor.proto"; +message OptionFoo { + string foo = 1; +} +extend google.protobuf.MessageOptions { + OptionFoo message_foo = 50000; +} diff --git a/private/bufpkg/bufimage/bufimageutil/testdata/imports/foo.txtar b/private/bufpkg/bufimage/bufimageutil/testdata/imports/foo.txtar new file mode 100644 index 0000000000..68c301a0f9 --- /dev/null +++ b/private/bufpkg/bufimage/bufimageutil/testdata/imports/foo.txtar @@ -0,0 +1,484 @@ +-- a.proto -- +syntax = "proto3"; +package pkg; +import "options.proto"; +message Foo { + Bar nested_bar = 2; + message Bar { + option (message_bar) = { bar: "str" }; + string bar = 1; + } +} +-- google/protobuf/descriptor.proto -- +syntax = "proto2"; +package google.protobuf; +option cc_enable_arenas = true; +option csharp_namespace = "Google.Protobuf.Reflection"; +option go_package = "google.golang.org/protobuf/types/descriptorpb"; +option java_outer_classname = "DescriptorProtos"; +option java_package = "com.google.protobuf"; +option objc_class_prefix = "GPB"; +option optimize_for = SPEED; +message DescriptorProto { + optional string name = 1; + repeated FieldDescriptorProto field = 2; + repeated DescriptorProto nested_type = 3; + repeated EnumDescriptorProto enum_type = 4; + repeated ExtensionRange extension_range = 5; + repeated FieldDescriptorProto extension = 6; + optional MessageOptions options = 7; + repeated OneofDescriptorProto oneof_decl = 8; + repeated ReservedRange reserved_range = 9; + repeated string reserved_name = 10; + message ExtensionRange { + optional int32 start = 1; + optional int32 end = 2; + optional ExtensionRangeOptions options = 3; + } + message ReservedRange { + optional int32 start = 1; + optional int32 end = 2; + } +} +message EnumDescriptorProto { + optional string name = 1; + repeated EnumValueDescriptorProto value = 2; + optional EnumOptions options = 3; + repeated EnumReservedRange reserved_range = 4; + repeated string reserved_name = 5; + message EnumReservedRange { + optional int32 start = 1; + optional int32 end = 2; + } +} +message EnumOptions { + optional bool allow_alias = 2; + optional bool deprecated = 3 [default = false]; + optional bool deprecated_legacy_json_field_conflicts = 6 [deprecated = true]; + optional FeatureSet features = 7; + repeated UninterpretedOption uninterpreted_option = 999; + extensions 1000 to max; + reserved 5; +} +message EnumValueDescriptorProto { + optional string name = 1; + optional int32 number = 2; + optional EnumValueOptions options = 3; +} +message EnumValueOptions { + optional bool deprecated = 1 [default = false]; + optional FeatureSet features = 2; + optional bool debug_redact = 3 [default = false]; + optional FieldOptions.FeatureSupport feature_support = 4; + repeated UninterpretedOption uninterpreted_option = 999; + extensions 1000 to max; +} +message ExtensionRangeOptions { + repeated Declaration declaration = 2 [retention = RETENTION_SOURCE]; + optional VerificationState verification = 3 [default = UNVERIFIED, retention = RETENTION_SOURCE]; + optional FeatureSet features = 50; + repeated UninterpretedOption uninterpreted_option = 999; + message Declaration { + optional int32 number = 1; + optional string full_name = 2; + optional string type = 3; + optional bool reserved = 5; + optional bool repeated = 6; + reserved 4; + } + enum VerificationState { + DECLARATION = 0; + UNVERIFIED = 1; + } + extensions 1000 to max; +} +message FeatureSet { + optional FieldPresence field_presence = 1 [ + edition_defaults = { value: "EXPLICIT", edition: EDITION_LEGACY }, + edition_defaults = { value: "IMPLICIT", edition: EDITION_PROTO3 }, + edition_defaults = { value: "EXPLICIT", edition: EDITION_2023 }, + feature_support = { edition_introduced: EDITION_2023 }, + retention = RETENTION_RUNTIME, + targets = TARGET_TYPE_FIELD, + targets = TARGET_TYPE_FILE + ]; + optional EnumType enum_type = 2 [ + edition_defaults = { value: "CLOSED", edition: EDITION_LEGACY }, + edition_defaults = { value: "OPEN", edition: EDITION_PROTO3 }, + feature_support = { edition_introduced: EDITION_2023 }, + retention = RETENTION_RUNTIME, + targets = TARGET_TYPE_ENUM, + targets = TARGET_TYPE_FILE + ]; + optional RepeatedFieldEncoding repeated_field_encoding = 3 [ + edition_defaults = { value: "EXPANDED", edition: EDITION_LEGACY }, + edition_defaults = { value: "PACKED", edition: EDITION_PROTO3 }, + feature_support = { edition_introduced: EDITION_2023 }, + retention = RETENTION_RUNTIME, + targets = TARGET_TYPE_FIELD, + targets = TARGET_TYPE_FILE + ]; + optional Utf8Validation utf8_validation = 4 [ + edition_defaults = { value: "NONE", edition: EDITION_LEGACY }, + edition_defaults = { value: "VERIFY", edition: EDITION_PROTO3 }, + feature_support = { edition_introduced: EDITION_2023 }, + retention = RETENTION_RUNTIME, + targets = TARGET_TYPE_FIELD, + targets = TARGET_TYPE_FILE + ]; + optional MessageEncoding message_encoding = 5 [ + edition_defaults = { value: "LENGTH_PREFIXED", edition: EDITION_LEGACY }, + feature_support = { edition_introduced: EDITION_2023 }, + retention = RETENTION_RUNTIME, + targets = TARGET_TYPE_FIELD, + targets = TARGET_TYPE_FILE + ]; + optional JsonFormat json_format = 6 [ + edition_defaults = { value: "LEGACY_BEST_EFFORT", edition: EDITION_LEGACY }, + edition_defaults = { value: "ALLOW", edition: EDITION_PROTO3 }, + feature_support = { edition_introduced: EDITION_2023 }, + retention = RETENTION_RUNTIME, + targets = TARGET_TYPE_MESSAGE, + targets = TARGET_TYPE_ENUM, + targets = TARGET_TYPE_FILE + ]; + enum EnumType { + ENUM_TYPE_UNKNOWN = 0; + OPEN = 1; + CLOSED = 2; + } + enum FieldPresence { + FIELD_PRESENCE_UNKNOWN = 0; + EXPLICIT = 1; + IMPLICIT = 2; + LEGACY_REQUIRED = 3; + } + enum JsonFormat { + JSON_FORMAT_UNKNOWN = 0; + ALLOW = 1; + LEGACY_BEST_EFFORT = 2; + } + enum MessageEncoding { + MESSAGE_ENCODING_UNKNOWN = 0; + LENGTH_PREFIXED = 1; + DELIMITED = 2; + } + enum RepeatedFieldEncoding { + REPEATED_FIELD_ENCODING_UNKNOWN = 0; + PACKED = 1; + EXPANDED = 2; + } + enum Utf8Validation { + UTF8_VALIDATION_UNKNOWN = 0; + VERIFY = 2; + NONE = 3; + reserved 1; + } + extensions 1000 to 9994 [ + declaration = { + number: 1000, + full_name: ".pb.cpp", + type: ".pb.CppFeatures" + }, + declaration = { + number: 1001, + full_name: ".pb.java", + type: ".pb.JavaFeatures" + }, + declaration = { + number: 1002, + full_name: ".pb.go", + type: ".pb.GoFeatures" + }, + declaration = { + number: 9990, + full_name: ".pb.proto1", + type: ".pb.Proto1Features" + } + ]; + extensions 9995 to 9999, 10000; + reserved 999; +} +message FeatureSetDefaults { + repeated FeatureSetEditionDefault defaults = 1; + optional Edition minimum_edition = 4; + optional Edition maximum_edition = 5; + message FeatureSetEditionDefault { + optional Edition edition = 3; + optional FeatureSet overridable_features = 4; + optional FeatureSet fixed_features = 5; + reserved 1, 2; + reserved "features"; + } +} +message FieldDescriptorProto { + optional string name = 1; + optional string extendee = 2; + optional int32 number = 3; + optional Label label = 4; + optional Type type = 5; + optional string type_name = 6; + optional string default_value = 7; + optional FieldOptions options = 8; + optional int32 oneof_index = 9; + optional string json_name = 10; + optional bool proto3_optional = 17; + enum Label { + LABEL_OPTIONAL = 1; + LABEL_REQUIRED = 2; + LABEL_REPEATED = 3; + } + enum Type { + TYPE_DOUBLE = 1; + TYPE_FLOAT = 2; + TYPE_INT64 = 3; + TYPE_UINT64 = 4; + TYPE_INT32 = 5; + TYPE_FIXED64 = 6; + TYPE_FIXED32 = 7; + TYPE_BOOL = 8; + TYPE_STRING = 9; + TYPE_GROUP = 10; + TYPE_MESSAGE = 11; + TYPE_BYTES = 12; + TYPE_UINT32 = 13; + TYPE_ENUM = 14; + TYPE_SFIXED32 = 15; + TYPE_SFIXED64 = 16; + TYPE_SINT32 = 17; + TYPE_SINT64 = 18; + } +} +message FieldOptions { + optional CType ctype = 1 [default = STRING]; + optional bool packed = 2; + optional bool deprecated = 3 [default = false]; + optional bool lazy = 5 [default = false]; + optional JSType jstype = 6 [default = JS_NORMAL]; + optional bool weak = 10 [default = false]; + optional bool unverified_lazy = 15 [default = false]; + optional bool debug_redact = 16 [default = false]; + optional OptionRetention retention = 17; + repeated OptionTargetType targets = 19; + repeated EditionDefault edition_defaults = 20; + optional FeatureSet features = 21; + optional FeatureSupport feature_support = 22; + repeated UninterpretedOption uninterpreted_option = 999; + message EditionDefault { + optional string value = 2; + optional Edition edition = 3; + } + message FeatureSupport { + optional Edition edition_introduced = 1; + optional Edition edition_deprecated = 2; + optional string deprecation_warning = 3; + optional Edition edition_removed = 4; + } + enum CType { + STRING = 0; + CORD = 1; + STRING_PIECE = 2; + } + enum JSType { + JS_NORMAL = 0; + JS_STRING = 1; + JS_NUMBER = 2; + } + enum OptionRetention { + RETENTION_UNKNOWN = 0; + RETENTION_RUNTIME = 1; + RETENTION_SOURCE = 2; + } + enum OptionTargetType { + TARGET_TYPE_UNKNOWN = 0; + TARGET_TYPE_FILE = 1; + TARGET_TYPE_EXTENSION_RANGE = 2; + TARGET_TYPE_MESSAGE = 3; + TARGET_TYPE_FIELD = 4; + TARGET_TYPE_ONEOF = 5; + TARGET_TYPE_ENUM = 6; + TARGET_TYPE_ENUM_ENTRY = 7; + TARGET_TYPE_SERVICE = 8; + TARGET_TYPE_METHOD = 9; + } + extensions 1000 to max; + reserved 4, 18; +} +message FileDescriptorProto { + optional string name = 1; + optional string package = 2; + repeated string dependency = 3; + repeated DescriptorProto message_type = 4; + repeated EnumDescriptorProto enum_type = 5; + repeated ServiceDescriptorProto service = 6; + repeated FieldDescriptorProto extension = 7; + optional FileOptions options = 8; + optional SourceCodeInfo source_code_info = 9; + repeated int32 public_dependency = 10; + repeated int32 weak_dependency = 11; + optional string syntax = 12; + optional Edition edition = 14; +} +message FileDescriptorSet { + repeated FileDescriptorProto file = 1; + extensions 536000000 [ + declaration = { + number: 536000000, + full_name: ".buf.descriptor.v1.buf_file_descriptor_set_extension", + type: ".buf.descriptor.v1.FileDescriptorSetExtension" + } + ]; +} +message FileOptions { + optional string java_package = 1; + optional string java_outer_classname = 8; + optional OptimizeMode optimize_for = 9 [default = SPEED]; + optional bool java_multiple_files = 10 [default = false]; + optional string go_package = 11; + optional bool cc_generic_services = 16 [default = false]; + optional bool java_generic_services = 17 [default = false]; + optional bool py_generic_services = 18 [default = false]; + optional bool java_generate_equals_and_hash = 20 [deprecated = true]; + optional bool deprecated = 23 [default = false]; + optional bool java_string_check_utf8 = 27 [default = false]; + optional bool cc_enable_arenas = 31 [default = true]; + optional string objc_class_prefix = 36; + optional string csharp_namespace = 37; + optional string swift_prefix = 39; + optional string php_class_prefix = 40; + optional string php_namespace = 41; + optional string php_metadata_namespace = 44; + optional string ruby_package = 45; + optional FeatureSet features = 50; + repeated UninterpretedOption uninterpreted_option = 999; + enum OptimizeMode { + SPEED = 1; + CODE_SIZE = 2; + LITE_RUNTIME = 3; + } + extensions 1000 to max; + reserved 38, 42; + reserved "php_generic_services"; +} +message GeneratedCodeInfo { + repeated Annotation annotation = 1; + message Annotation { + repeated int32 path = 1 [packed = true]; + optional string source_file = 2; + optional int32 begin = 3; + optional int32 end = 4; + optional Semantic semantic = 5; + enum Semantic { + NONE = 0; + SET = 1; + ALIAS = 2; + } + } +} +message MessageOptions { + optional bool message_set_wire_format = 1 [default = false]; + optional bool no_standard_descriptor_accessor = 2 [default = false]; + optional bool deprecated = 3 [default = false]; + optional bool map_entry = 7; + optional bool deprecated_legacy_json_field_conflicts = 11 [deprecated = true]; + optional FeatureSet features = 12; + repeated UninterpretedOption uninterpreted_option = 999; + extensions 1000 to max; + reserved 4, 5, 6, 8, 9; +} +message MethodDescriptorProto { + optional string name = 1; + optional string input_type = 2; + optional string output_type = 3; + optional MethodOptions options = 4; + optional bool client_streaming = 5 [default = false]; + optional bool server_streaming = 6 [default = false]; +} +message MethodOptions { + optional bool deprecated = 33 [default = false]; + optional IdempotencyLevel idempotency_level = 34 [default = IDEMPOTENCY_UNKNOWN]; + optional FeatureSet features = 35; + repeated UninterpretedOption uninterpreted_option = 999; + enum IdempotencyLevel { + IDEMPOTENCY_UNKNOWN = 0; + NO_SIDE_EFFECTS = 1; + IDEMPOTENT = 2; + } + extensions 1000 to max; +} +message OneofDescriptorProto { + optional string name = 1; + optional OneofOptions options = 2; +} +message OneofOptions { + optional FeatureSet features = 1; + repeated UninterpretedOption uninterpreted_option = 999; + extensions 1000 to max; +} +message ServiceDescriptorProto { + optional string name = 1; + repeated MethodDescriptorProto method = 2; + optional ServiceOptions options = 3; +} +message ServiceOptions { + optional bool deprecated = 33 [default = false]; + optional FeatureSet features = 34; + repeated UninterpretedOption uninterpreted_option = 999; + extensions 1000 to max; +} +message SourceCodeInfo { + repeated Location location = 1; + message Location { + repeated int32 path = 1 [packed = true]; + repeated int32 span = 2 [packed = true]; + optional string leading_comments = 3; + optional string trailing_comments = 4; + repeated string leading_detached_comments = 6; + } + extensions 536000000 [ + declaration = { + number: 536000000, + full_name: ".buf.descriptor.v1.buf_source_code_info_extension", + type: ".buf.descriptor.v1.SourceCodeInfoExtension" + } + ]; +} +message UninterpretedOption { + repeated NamePart name = 2; + optional string identifier_value = 3; + optional uint64 positive_int_value = 4; + optional int64 negative_int_value = 5; + optional double double_value = 6; + optional bytes string_value = 7; + optional string aggregate_value = 8; + message NamePart { + required string name_part = 1; + required bool is_extension = 2; + } +} +enum Edition { + EDITION_UNKNOWN = 0; + EDITION_1_TEST_ONLY = 1; + EDITION_2_TEST_ONLY = 2; + EDITION_LEGACY = 900; + EDITION_PROTO2 = 998; + EDITION_PROTO3 = 999; + EDITION_2023 = 1000; + EDITION_2024 = 1001; + EDITION_99997_TEST_ONLY = 99997; + EDITION_99998_TEST_ONLY = 99998; + EDITION_99999_TEST_ONLY = 99999; + EDITION_MAX = 2147483647; +} +-- options.proto -- +syntax = "proto3"; +import "google/protobuf/descriptor.proto"; +message OptionBar { + string bar = 1; +} +message OptionFoo { + string foo = 1; +} +extend google.protobuf.MessageOptions { + OptionBar message_bar = 50001; +} diff --git a/private/bufpkg/bufimage/bufimageutil/testdata/imports/foo_bar.txtar b/private/bufpkg/bufimage/bufimageutil/testdata/imports/foo_bar.txtar new file mode 100644 index 0000000000..8209070bbc --- /dev/null +++ b/private/bufpkg/bufimage/bufimageutil/testdata/imports/foo_bar.txtar @@ -0,0 +1,9 @@ +-- a.proto -- +syntax = "proto3"; +package pkg; +message Foo { + Bar nested_bar = 2; + message Bar { + string bar = 1; + } +} diff --git a/private/bufpkg/bufimage/bufimageutil/testdata/imports/options.proto b/private/bufpkg/bufimage/bufimageutil/testdata/imports/options.proto new file mode 100644 index 0000000000..e2ed89085a --- /dev/null +++ b/private/bufpkg/bufimage/bufimageutil/testdata/imports/options.proto @@ -0,0 +1,14 @@ +syntax = "proto3"; +import "google/protobuf/descriptor.proto"; + +message OptionFoo { + string foo = 1; +} +message OptionBar { + string bar = 1; +} + +extend google.protobuf.MessageOptions { + optional OptionFoo message_foo = 50000; + optional OptionBar message_bar = 50001; +} diff --git a/private/bufpkg/bufimage/bufimageutil/testdata/nesting/enum.exclude.txtar b/private/bufpkg/bufimage/bufimageutil/testdata/nesting/enum.exclude.txtar new file mode 100644 index 0000000000..e5108d3007 --- /dev/null +++ b/private/bufpkg/bufimage/bufimageutil/testdata/nesting/enum.exclude.txtar @@ -0,0 +1,26 @@ +-- a.proto -- +syntax = "proto3"; +package pkg; +message Bar { + Foo.NestedFoo nested_foo = 2; + enum NestedBarEnum { + NESTED_BAR_ENUM_X = 0; + NESTED_BAR_ENUM_Y = 1; + } +} +message Baz { + Bar.NestedBarEnum nested_bar_enum = 1; +} +message Foo { + string x = 1; + NestedFoo nested_foo = 2; + message NestedButNotUsed { + string nested_but_not_used = 1; + } + message NestedFoo { + string nested_x = 1; + message NestedNestedFoo { + string nested_nested_x = 1; + } + } +} diff --git a/private/bufpkg/bufimage/bufimageutil/testdata/nesting/message.exclude.txtar b/private/bufpkg/bufimage/bufimageutil/testdata/nesting/message.exclude.txtar new file mode 100644 index 0000000000..2a5c092a2d --- /dev/null +++ b/private/bufpkg/bufimage/bufimageutil/testdata/nesting/message.exclude.txtar @@ -0,0 +1,17 @@ +-- a.proto -- +syntax = "proto3"; +package pkg; +message Bar { + FooEnum foo_enum = 1; + enum NestedBarEnum { + NESTED_BAR_ENUM_X = 0; + NESTED_BAR_ENUM_Y = 1; + } +} +message Baz { + Bar.NestedBarEnum nested_bar_enum = 1; +} +enum FooEnum { + FOO_ENUM_X = 0; + FOO_ENUM_Y = 1; +} diff --git a/private/bufpkg/bufimage/bufimageutil/testdata/nesting/mixed.txtar b/private/bufpkg/bufimage/bufimageutil/testdata/nesting/mixed.txtar new file mode 100644 index 0000000000..c580e345bd --- /dev/null +++ b/private/bufpkg/bufimage/bufimageutil/testdata/nesting/mixed.txtar @@ -0,0 +1,10 @@ +-- a.proto -- +syntax = "proto3"; +package pkg; +message Foo { + string x = 1; +} +enum FooEnum { + FOO_ENUM_X = 0; + FOO_ENUM_Y = 1; +} diff --git a/private/bufpkg/bufimage/bufimageutil/testdata/nesting/recursenested.exclude.txtar b/private/bufpkg/bufimage/bufimageutil/testdata/nesting/recursenested.exclude.txtar new file mode 100644 index 0000000000..48a3ce2508 --- /dev/null +++ b/private/bufpkg/bufimage/bufimageutil/testdata/nesting/recursenested.exclude.txtar @@ -0,0 +1,28 @@ +-- a.proto -- +syntax = "proto3"; +package pkg; +message Bar { + FooEnum foo_enum = 1; + Foo.NestedFoo nested_foo = 2; + enum NestedBarEnum { + NESTED_BAR_ENUM_X = 0; + NESTED_BAR_ENUM_Y = 1; + } +} +message Baz { + Bar.NestedBarEnum nested_bar_enum = 1; +} +message Foo { + string x = 1; + NestedFoo nested_foo = 2; + message NestedButNotUsed { + string nested_but_not_used = 1; + } + message NestedFoo { + string nested_x = 1; + } +} +enum FooEnum { + FOO_ENUM_X = 0; + FOO_ENUM_Y = 1; +} diff --git a/private/bufpkg/bufimage/bufimageutil/testdata/nesting/usingother.exclude.txtar b/private/bufpkg/bufimage/bufimageutil/testdata/nesting/usingother.exclude.txtar new file mode 100644 index 0000000000..bcff3d7385 --- /dev/null +++ b/private/bufpkg/bufimage/bufimageutil/testdata/nesting/usingother.exclude.txtar @@ -0,0 +1,28 @@ +-- a.proto -- +syntax = "proto3"; +package pkg; +message Bar { + FooEnum foo_enum = 1; + Foo.NestedFoo nested_foo = 2; + enum NestedBarEnum { + NESTED_BAR_ENUM_X = 0; + NESTED_BAR_ENUM_Y = 1; + } +} +message Foo { + string x = 1; + NestedFoo nested_foo = 2; + message NestedButNotUsed { + string nested_but_not_used = 1; + } + message NestedFoo { + string nested_x = 1; + message NestedNestedFoo { + string nested_nested_x = 1; + } + } +} +enum FooEnum { + FOO_ENUM_X = 0; + FOO_ENUM_Y = 1; +} diff --git a/private/bufpkg/bufimage/bufimageutil/testdata/oneofs/a.proto b/private/bufpkg/bufimage/bufimageutil/testdata/oneofs/a.proto new file mode 100644 index 0000000000..c3bc638414 --- /dev/null +++ b/private/bufpkg/bufimage/bufimageutil/testdata/oneofs/a.proto @@ -0,0 +1,31 @@ +syntax = "proto3"; +package pkg; +import "google/protobuf/descriptor.proto"; + +message Foo { + oneof oneofFoo { + option (oneof_foo) = "str"; + string foo = 1; + FooEnum foo_enum = 2; + } + oneof oneofBar { + option (oneof_bar) = "str"; + Bar bar = 3; + Bar.BarNested bar_nested = 4; + } +} + +enum FooEnum { + FOO_ENUM_X = 0; + FOO_ENUM_Y = 1; +} +message Bar { + string bar = 1; + message BarNested { + string bar = 1; + } +} +extend google.protobuf.OneofOptions { + optional string oneof_foo = 50000; + optional string oneof_bar = 50001; +} diff --git a/private/bufpkg/bufimage/bufimageutil/testdata/oneofs/pkg.Foo.exclude-bar.txtar b/private/bufpkg/bufimage/bufimageutil/testdata/oneofs/pkg.Foo.exclude-bar.txtar new file mode 100644 index 0000000000..acaf7c9a2a --- /dev/null +++ b/private/bufpkg/bufimage/bufimageutil/testdata/oneofs/pkg.Foo.exclude-bar.txtar @@ -0,0 +1,148 @@ +-- a.proto -- +syntax = "proto3"; +package pkg; +import "google/protobuf/descriptor.proto"; +message Foo { + oneof oneofFoo { + option (oneof_foo) = "str"; + string foo = 1; + } +} +extend google.protobuf.OneofOptions { + string oneof_foo = 50000; +} +-- google/protobuf/descriptor.proto -- +syntax = "proto2"; +package google.protobuf; +option cc_enable_arenas = true; +option csharp_namespace = "Google.Protobuf.Reflection"; +option go_package = "google.golang.org/protobuf/types/descriptorpb"; +option java_outer_classname = "DescriptorProtos"; +option java_package = "com.google.protobuf"; +option objc_class_prefix = "GPB"; +option optimize_for = SPEED; +message FeatureSet { + optional FieldPresence field_presence = 1 [ + edition_defaults = { value: "EXPLICIT", edition: EDITION_LEGACY }, + edition_defaults = { value: "IMPLICIT", edition: EDITION_PROTO3 }, + edition_defaults = { value: "EXPLICIT", edition: EDITION_2023 }, + feature_support = { edition_introduced: EDITION_2023 }, + retention = RETENTION_RUNTIME, + targets = TARGET_TYPE_FIELD, + targets = TARGET_TYPE_FILE + ]; + optional EnumType enum_type = 2 [ + edition_defaults = { value: "CLOSED", edition: EDITION_LEGACY }, + edition_defaults = { value: "OPEN", edition: EDITION_PROTO3 }, + feature_support = { edition_introduced: EDITION_2023 }, + retention = RETENTION_RUNTIME, + targets = TARGET_TYPE_ENUM, + targets = TARGET_TYPE_FILE + ]; + optional RepeatedFieldEncoding repeated_field_encoding = 3 [ + edition_defaults = { value: "EXPANDED", edition: EDITION_LEGACY }, + edition_defaults = { value: "PACKED", edition: EDITION_PROTO3 }, + feature_support = { edition_introduced: EDITION_2023 }, + retention = RETENTION_RUNTIME, + targets = TARGET_TYPE_FIELD, + targets = TARGET_TYPE_FILE + ]; + optional Utf8Validation utf8_validation = 4 [ + edition_defaults = { value: "NONE", edition: EDITION_LEGACY }, + edition_defaults = { value: "VERIFY", edition: EDITION_PROTO3 }, + feature_support = { edition_introduced: EDITION_2023 }, + retention = RETENTION_RUNTIME, + targets = TARGET_TYPE_FIELD, + targets = TARGET_TYPE_FILE + ]; + optional MessageEncoding message_encoding = 5 [ + edition_defaults = { value: "LENGTH_PREFIXED", edition: EDITION_LEGACY }, + feature_support = { edition_introduced: EDITION_2023 }, + retention = RETENTION_RUNTIME, + targets = TARGET_TYPE_FIELD, + targets = TARGET_TYPE_FILE + ]; + optional JsonFormat json_format = 6 [ + edition_defaults = { value: "LEGACY_BEST_EFFORT", edition: EDITION_LEGACY }, + edition_defaults = { value: "ALLOW", edition: EDITION_PROTO3 }, + feature_support = { edition_introduced: EDITION_2023 }, + retention = RETENTION_RUNTIME, + targets = TARGET_TYPE_MESSAGE, + targets = TARGET_TYPE_ENUM, + targets = TARGET_TYPE_FILE + ]; + enum EnumType { + ENUM_TYPE_UNKNOWN = 0; + OPEN = 1; + CLOSED = 2; + } + enum FieldPresence { + FIELD_PRESENCE_UNKNOWN = 0; + EXPLICIT = 1; + IMPLICIT = 2; + LEGACY_REQUIRED = 3; + } + enum JsonFormat { + JSON_FORMAT_UNKNOWN = 0; + ALLOW = 1; + LEGACY_BEST_EFFORT = 2; + } + enum MessageEncoding { + MESSAGE_ENCODING_UNKNOWN = 0; + LENGTH_PREFIXED = 1; + DELIMITED = 2; + } + enum RepeatedFieldEncoding { + REPEATED_FIELD_ENCODING_UNKNOWN = 0; + PACKED = 1; + EXPANDED = 2; + } + enum Utf8Validation { + UTF8_VALIDATION_UNKNOWN = 0; + VERIFY = 2; + NONE = 3; + reserved 1; + } + extensions 1000 to 9994 [ + declaration = { + number: 1000, + full_name: ".pb.cpp", + type: ".pb.CppFeatures" + }, + declaration = { + number: 1001, + full_name: ".pb.java", + type: ".pb.JavaFeatures" + }, + declaration = { + number: 1002, + full_name: ".pb.go", + type: ".pb.GoFeatures" + }, + declaration = { + number: 9990, + full_name: ".pb.proto1", + type: ".pb.Proto1Features" + } + ]; + extensions 9995 to 9999, 10000; + reserved 999; +} +message OneofOptions { + optional FeatureSet features = 1; + repeated UninterpretedOption uninterpreted_option = 999; + extensions 1000 to max; +} +message UninterpretedOption { + repeated NamePart name = 2; + optional string identifier_value = 3; + optional uint64 positive_int_value = 4; + optional int64 negative_int_value = 5; + optional double double_value = 6; + optional bytes string_value = 7; + optional string aggregate_value = 8; + message NamePart { + required string name_part = 1; + required bool is_extension = 2; + } +} diff --git a/private/bufpkg/bufimage/bufimageutil/testdata/oneofs/pkg.Foo.exclude-partial.txtar b/private/bufpkg/bufimage/bufimageutil/testdata/oneofs/pkg.Foo.exclude-partial.txtar new file mode 100644 index 0000000000..e10eaf1a5c --- /dev/null +++ b/private/bufpkg/bufimage/bufimageutil/testdata/oneofs/pkg.Foo.exclude-partial.txtar @@ -0,0 +1,156 @@ +-- a.proto -- +syntax = "proto3"; +package pkg; +import "google/protobuf/descriptor.proto"; +message Bar { + string bar = 1; +} +message Foo { + oneof oneofFoo { + option (oneof_foo) = "str"; + string foo = 1; + } + oneof oneofBar { + option (oneof_bar) = "str"; + Bar bar = 3; + } +} +extend google.protobuf.OneofOptions { + string oneof_foo = 50000; + string oneof_bar = 50001; +} +-- google/protobuf/descriptor.proto -- +syntax = "proto2"; +package google.protobuf; +option cc_enable_arenas = true; +option csharp_namespace = "Google.Protobuf.Reflection"; +option go_package = "google.golang.org/protobuf/types/descriptorpb"; +option java_outer_classname = "DescriptorProtos"; +option java_package = "com.google.protobuf"; +option objc_class_prefix = "GPB"; +option optimize_for = SPEED; +message FeatureSet { + optional FieldPresence field_presence = 1 [ + edition_defaults = { value: "EXPLICIT", edition: EDITION_LEGACY }, + edition_defaults = { value: "IMPLICIT", edition: EDITION_PROTO3 }, + edition_defaults = { value: "EXPLICIT", edition: EDITION_2023 }, + feature_support = { edition_introduced: EDITION_2023 }, + retention = RETENTION_RUNTIME, + targets = TARGET_TYPE_FIELD, + targets = TARGET_TYPE_FILE + ]; + optional EnumType enum_type = 2 [ + edition_defaults = { value: "CLOSED", edition: EDITION_LEGACY }, + edition_defaults = { value: "OPEN", edition: EDITION_PROTO3 }, + feature_support = { edition_introduced: EDITION_2023 }, + retention = RETENTION_RUNTIME, + targets = TARGET_TYPE_ENUM, + targets = TARGET_TYPE_FILE + ]; + optional RepeatedFieldEncoding repeated_field_encoding = 3 [ + edition_defaults = { value: "EXPANDED", edition: EDITION_LEGACY }, + edition_defaults = { value: "PACKED", edition: EDITION_PROTO3 }, + feature_support = { edition_introduced: EDITION_2023 }, + retention = RETENTION_RUNTIME, + targets = TARGET_TYPE_FIELD, + targets = TARGET_TYPE_FILE + ]; + optional Utf8Validation utf8_validation = 4 [ + edition_defaults = { value: "NONE", edition: EDITION_LEGACY }, + edition_defaults = { value: "VERIFY", edition: EDITION_PROTO3 }, + feature_support = { edition_introduced: EDITION_2023 }, + retention = RETENTION_RUNTIME, + targets = TARGET_TYPE_FIELD, + targets = TARGET_TYPE_FILE + ]; + optional MessageEncoding message_encoding = 5 [ + edition_defaults = { value: "LENGTH_PREFIXED", edition: EDITION_LEGACY }, + feature_support = { edition_introduced: EDITION_2023 }, + retention = RETENTION_RUNTIME, + targets = TARGET_TYPE_FIELD, + targets = TARGET_TYPE_FILE + ]; + optional JsonFormat json_format = 6 [ + edition_defaults = { value: "LEGACY_BEST_EFFORT", edition: EDITION_LEGACY }, + edition_defaults = { value: "ALLOW", edition: EDITION_PROTO3 }, + feature_support = { edition_introduced: EDITION_2023 }, + retention = RETENTION_RUNTIME, + targets = TARGET_TYPE_MESSAGE, + targets = TARGET_TYPE_ENUM, + targets = TARGET_TYPE_FILE + ]; + enum EnumType { + ENUM_TYPE_UNKNOWN = 0; + OPEN = 1; + CLOSED = 2; + } + enum FieldPresence { + FIELD_PRESENCE_UNKNOWN = 0; + EXPLICIT = 1; + IMPLICIT = 2; + LEGACY_REQUIRED = 3; + } + enum JsonFormat { + JSON_FORMAT_UNKNOWN = 0; + ALLOW = 1; + LEGACY_BEST_EFFORT = 2; + } + enum MessageEncoding { + MESSAGE_ENCODING_UNKNOWN = 0; + LENGTH_PREFIXED = 1; + DELIMITED = 2; + } + enum RepeatedFieldEncoding { + REPEATED_FIELD_ENCODING_UNKNOWN = 0; + PACKED = 1; + EXPANDED = 2; + } + enum Utf8Validation { + UTF8_VALIDATION_UNKNOWN = 0; + VERIFY = 2; + NONE = 3; + reserved 1; + } + extensions 1000 to 9994 [ + declaration = { + number: 1000, + full_name: ".pb.cpp", + type: ".pb.CppFeatures" + }, + declaration = { + number: 1001, + full_name: ".pb.java", + type: ".pb.JavaFeatures" + }, + declaration = { + number: 1002, + full_name: ".pb.go", + type: ".pb.GoFeatures" + }, + declaration = { + number: 9990, + full_name: ".pb.proto1", + type: ".pb.Proto1Features" + } + ]; + extensions 9995 to 9999, 10000; + reserved 999; +} +message OneofOptions { + optional FeatureSet features = 1; + repeated UninterpretedOption uninterpreted_option = 999; + extensions 1000 to max; +} +message UninterpretedOption { + repeated NamePart name = 2; + optional string identifier_value = 3; + optional uint64 positive_int_value = 4; + optional int64 negative_int_value = 5; + optional double double_value = 6; + optional bytes string_value = 7; + optional string aggregate_value = 8; + message NamePart { + required string name_part = 1; + required bool is_extension = 2; + } +} diff --git a/private/bufpkg/bufimage/bufimageutil/testdata/options/all.exclude.txtar b/private/bufpkg/bufimage/bufimageutil/testdata/options/all.exclude.txtar new file mode 100644 index 0000000000..0e890cf7f5 --- /dev/null +++ b/private/bufpkg/bufimage/bufimageutil/testdata/options/all.exclude.txtar @@ -0,0 +1,521 @@ +-- a.proto -- +syntax = "proto2"; +package pkg; +import "options.proto"; +option (UsedOption.file_baz) = "str"; +option (UsedOption.file_foo) = { foo: "str" }; +message Empty { +} +-- google/protobuf/descriptor.proto -- +syntax = "proto2"; +package google.protobuf; +option cc_enable_arenas = true; +option csharp_namespace = "Google.Protobuf.Reflection"; +option go_package = "google.golang.org/protobuf/types/descriptorpb"; +option java_outer_classname = "DescriptorProtos"; +option java_package = "com.google.protobuf"; +option objc_class_prefix = "GPB"; +option optimize_for = SPEED; +message DescriptorProto { + optional string name = 1; + repeated FieldDescriptorProto field = 2; + repeated DescriptorProto nested_type = 3; + repeated EnumDescriptorProto enum_type = 4; + repeated ExtensionRange extension_range = 5; + repeated FieldDescriptorProto extension = 6; + optional MessageOptions options = 7; + repeated OneofDescriptorProto oneof_decl = 8; + repeated ReservedRange reserved_range = 9; + repeated string reserved_name = 10; + message ExtensionRange { + optional int32 start = 1; + optional int32 end = 2; + optional ExtensionRangeOptions options = 3; + } + message ReservedRange { + optional int32 start = 1; + optional int32 end = 2; + } +} +message EnumDescriptorProto { + optional string name = 1; + repeated EnumValueDescriptorProto value = 2; + optional EnumOptions options = 3; + repeated EnumReservedRange reserved_range = 4; + repeated string reserved_name = 5; + message EnumReservedRange { + optional int32 start = 1; + optional int32 end = 2; + } +} +message EnumOptions { + optional bool allow_alias = 2; + optional bool deprecated = 3 [default = false]; + optional bool deprecated_legacy_json_field_conflicts = 6 [deprecated = true]; + optional FeatureSet features = 7; + repeated UninterpretedOption uninterpreted_option = 999; + extensions 1000 to max; + reserved 5; +} +message EnumValueDescriptorProto { + optional string name = 1; + optional int32 number = 2; + optional EnumValueOptions options = 3; +} +message EnumValueOptions { + optional bool deprecated = 1 [default = false]; + optional FeatureSet features = 2; + optional bool debug_redact = 3 [default = false]; + optional FieldOptions.FeatureSupport feature_support = 4; + repeated UninterpretedOption uninterpreted_option = 999; + extensions 1000 to max; +} +message ExtensionRangeOptions { + repeated Declaration declaration = 2 [retention = RETENTION_SOURCE]; + optional VerificationState verification = 3 [default = UNVERIFIED, retention = RETENTION_SOURCE]; + optional FeatureSet features = 50; + repeated UninterpretedOption uninterpreted_option = 999; + message Declaration { + optional int32 number = 1; + optional string full_name = 2; + optional string type = 3; + optional bool reserved = 5; + optional bool repeated = 6; + reserved 4; + } + enum VerificationState { + DECLARATION = 0; + UNVERIFIED = 1; + } + extensions 1000 to max; +} +message FeatureSet { + optional FieldPresence field_presence = 1 [ + edition_defaults = { value: "EXPLICIT", edition: EDITION_LEGACY }, + edition_defaults = { value: "IMPLICIT", edition: EDITION_PROTO3 }, + edition_defaults = { value: "EXPLICIT", edition: EDITION_2023 }, + feature_support = { edition_introduced: EDITION_2023 }, + retention = RETENTION_RUNTIME, + targets = TARGET_TYPE_FIELD, + targets = TARGET_TYPE_FILE + ]; + optional EnumType enum_type = 2 [ + edition_defaults = { value: "CLOSED", edition: EDITION_LEGACY }, + edition_defaults = { value: "OPEN", edition: EDITION_PROTO3 }, + feature_support = { edition_introduced: EDITION_2023 }, + retention = RETENTION_RUNTIME, + targets = TARGET_TYPE_ENUM, + targets = TARGET_TYPE_FILE + ]; + optional RepeatedFieldEncoding repeated_field_encoding = 3 [ + edition_defaults = { value: "EXPANDED", edition: EDITION_LEGACY }, + edition_defaults = { value: "PACKED", edition: EDITION_PROTO3 }, + feature_support = { edition_introduced: EDITION_2023 }, + retention = RETENTION_RUNTIME, + targets = TARGET_TYPE_FIELD, + targets = TARGET_TYPE_FILE + ]; + optional Utf8Validation utf8_validation = 4 [ + edition_defaults = { value: "NONE", edition: EDITION_LEGACY }, + edition_defaults = { value: "VERIFY", edition: EDITION_PROTO3 }, + feature_support = { edition_introduced: EDITION_2023 }, + retention = RETENTION_RUNTIME, + targets = TARGET_TYPE_FIELD, + targets = TARGET_TYPE_FILE + ]; + optional MessageEncoding message_encoding = 5 [ + edition_defaults = { value: "LENGTH_PREFIXED", edition: EDITION_LEGACY }, + feature_support = { edition_introduced: EDITION_2023 }, + retention = RETENTION_RUNTIME, + targets = TARGET_TYPE_FIELD, + targets = TARGET_TYPE_FILE + ]; + optional JsonFormat json_format = 6 [ + edition_defaults = { value: "LEGACY_BEST_EFFORT", edition: EDITION_LEGACY }, + edition_defaults = { value: "ALLOW", edition: EDITION_PROTO3 }, + feature_support = { edition_introduced: EDITION_2023 }, + retention = RETENTION_RUNTIME, + targets = TARGET_TYPE_MESSAGE, + targets = TARGET_TYPE_ENUM, + targets = TARGET_TYPE_FILE + ]; + enum EnumType { + ENUM_TYPE_UNKNOWN = 0; + OPEN = 1; + CLOSED = 2; + } + enum FieldPresence { + FIELD_PRESENCE_UNKNOWN = 0; + EXPLICIT = 1; + IMPLICIT = 2; + LEGACY_REQUIRED = 3; + } + enum JsonFormat { + JSON_FORMAT_UNKNOWN = 0; + ALLOW = 1; + LEGACY_BEST_EFFORT = 2; + } + enum MessageEncoding { + MESSAGE_ENCODING_UNKNOWN = 0; + LENGTH_PREFIXED = 1; + DELIMITED = 2; + } + enum RepeatedFieldEncoding { + REPEATED_FIELD_ENCODING_UNKNOWN = 0; + PACKED = 1; + EXPANDED = 2; + } + enum Utf8Validation { + UTF8_VALIDATION_UNKNOWN = 0; + VERIFY = 2; + NONE = 3; + reserved 1; + } + extensions 1000 to 9994 [ + declaration = { + number: 1000, + full_name: ".pb.cpp", + type: ".pb.CppFeatures" + }, + declaration = { + number: 1001, + full_name: ".pb.java", + type: ".pb.JavaFeatures" + }, + declaration = { + number: 1002, + full_name: ".pb.go", + type: ".pb.GoFeatures" + }, + declaration = { + number: 9990, + full_name: ".pb.proto1", + type: ".pb.Proto1Features" + } + ]; + extensions 9995 to 9999, 10000; + reserved 999; +} +message FeatureSetDefaults { + repeated FeatureSetEditionDefault defaults = 1; + optional Edition minimum_edition = 4; + optional Edition maximum_edition = 5; + message FeatureSetEditionDefault { + optional Edition edition = 3; + optional FeatureSet overridable_features = 4; + optional FeatureSet fixed_features = 5; + reserved 1, 2; + reserved "features"; + } +} +message FieldDescriptorProto { + optional string name = 1; + optional string extendee = 2; + optional int32 number = 3; + optional Label label = 4; + optional Type type = 5; + optional string type_name = 6; + optional string default_value = 7; + optional FieldOptions options = 8; + optional int32 oneof_index = 9; + optional string json_name = 10; + optional bool proto3_optional = 17; + enum Label { + LABEL_OPTIONAL = 1; + LABEL_REQUIRED = 2; + LABEL_REPEATED = 3; + } + enum Type { + TYPE_DOUBLE = 1; + TYPE_FLOAT = 2; + TYPE_INT64 = 3; + TYPE_UINT64 = 4; + TYPE_INT32 = 5; + TYPE_FIXED64 = 6; + TYPE_FIXED32 = 7; + TYPE_BOOL = 8; + TYPE_STRING = 9; + TYPE_GROUP = 10; + TYPE_MESSAGE = 11; + TYPE_BYTES = 12; + TYPE_UINT32 = 13; + TYPE_ENUM = 14; + TYPE_SFIXED32 = 15; + TYPE_SFIXED64 = 16; + TYPE_SINT32 = 17; + TYPE_SINT64 = 18; + } +} +message FieldOptions { + optional CType ctype = 1 [default = STRING]; + optional bool packed = 2; + optional bool deprecated = 3 [default = false]; + optional bool lazy = 5 [default = false]; + optional JSType jstype = 6 [default = JS_NORMAL]; + optional bool weak = 10 [default = false]; + optional bool unverified_lazy = 15 [default = false]; + optional bool debug_redact = 16 [default = false]; + optional OptionRetention retention = 17; + repeated OptionTargetType targets = 19; + repeated EditionDefault edition_defaults = 20; + optional FeatureSet features = 21; + optional FeatureSupport feature_support = 22; + repeated UninterpretedOption uninterpreted_option = 999; + message EditionDefault { + optional string value = 2; + optional Edition edition = 3; + } + message FeatureSupport { + optional Edition edition_introduced = 1; + optional Edition edition_deprecated = 2; + optional string deprecation_warning = 3; + optional Edition edition_removed = 4; + } + enum CType { + STRING = 0; + CORD = 1; + STRING_PIECE = 2; + } + enum JSType { + JS_NORMAL = 0; + JS_STRING = 1; + JS_NUMBER = 2; + } + enum OptionRetention { + RETENTION_UNKNOWN = 0; + RETENTION_RUNTIME = 1; + RETENTION_SOURCE = 2; + } + enum OptionTargetType { + TARGET_TYPE_UNKNOWN = 0; + TARGET_TYPE_FILE = 1; + TARGET_TYPE_EXTENSION_RANGE = 2; + TARGET_TYPE_MESSAGE = 3; + TARGET_TYPE_FIELD = 4; + TARGET_TYPE_ONEOF = 5; + TARGET_TYPE_ENUM = 6; + TARGET_TYPE_ENUM_ENTRY = 7; + TARGET_TYPE_SERVICE = 8; + TARGET_TYPE_METHOD = 9; + } + extensions 1000 to max; + reserved 4, 18; +} +message FileDescriptorProto { + optional string name = 1; + optional string package = 2; + repeated string dependency = 3; + repeated DescriptorProto message_type = 4; + repeated EnumDescriptorProto enum_type = 5; + repeated ServiceDescriptorProto service = 6; + repeated FieldDescriptorProto extension = 7; + optional FileOptions options = 8; + optional SourceCodeInfo source_code_info = 9; + repeated int32 public_dependency = 10; + repeated int32 weak_dependency = 11; + optional string syntax = 12; + optional Edition edition = 14; +} +message FileDescriptorSet { + repeated FileDescriptorProto file = 1; + extensions 536000000 [ + declaration = { + number: 536000000, + full_name: ".buf.descriptor.v1.buf_file_descriptor_set_extension", + type: ".buf.descriptor.v1.FileDescriptorSetExtension" + } + ]; +} +message FileOptions { + optional string java_package = 1; + optional string java_outer_classname = 8; + optional OptimizeMode optimize_for = 9 [default = SPEED]; + optional bool java_multiple_files = 10 [default = false]; + optional string go_package = 11; + optional bool cc_generic_services = 16 [default = false]; + optional bool java_generic_services = 17 [default = false]; + optional bool py_generic_services = 18 [default = false]; + optional bool java_generate_equals_and_hash = 20 [deprecated = true]; + optional bool deprecated = 23 [default = false]; + optional bool java_string_check_utf8 = 27 [default = false]; + optional bool cc_enable_arenas = 31 [default = true]; + optional string objc_class_prefix = 36; + optional string csharp_namespace = 37; + optional string swift_prefix = 39; + optional string php_class_prefix = 40; + optional string php_namespace = 41; + optional string php_metadata_namespace = 44; + optional string ruby_package = 45; + optional FeatureSet features = 50; + repeated UninterpretedOption uninterpreted_option = 999; + enum OptimizeMode { + SPEED = 1; + CODE_SIZE = 2; + LITE_RUNTIME = 3; + } + extensions 1000 to max; + reserved 38, 42; + reserved "php_generic_services"; +} +message GeneratedCodeInfo { + repeated Annotation annotation = 1; + message Annotation { + repeated int32 path = 1 [packed = true]; + optional string source_file = 2; + optional int32 begin = 3; + optional int32 end = 4; + optional Semantic semantic = 5; + enum Semantic { + NONE = 0; + SET = 1; + ALIAS = 2; + } + } +} +message MessageOptions { + optional bool message_set_wire_format = 1 [default = false]; + optional bool no_standard_descriptor_accessor = 2 [default = false]; + optional bool deprecated = 3 [default = false]; + optional bool map_entry = 7; + optional bool deprecated_legacy_json_field_conflicts = 11 [deprecated = true]; + optional FeatureSet features = 12; + repeated UninterpretedOption uninterpreted_option = 999; + extensions 1000 to max; + reserved 4, 5, 6, 8, 9; +} +message MethodDescriptorProto { + optional string name = 1; + optional string input_type = 2; + optional string output_type = 3; + optional MethodOptions options = 4; + optional bool client_streaming = 5 [default = false]; + optional bool server_streaming = 6 [default = false]; +} +message MethodOptions { + optional bool deprecated = 33 [default = false]; + optional IdempotencyLevel idempotency_level = 34 [default = IDEMPOTENCY_UNKNOWN]; + optional FeatureSet features = 35; + repeated UninterpretedOption uninterpreted_option = 999; + enum IdempotencyLevel { + IDEMPOTENCY_UNKNOWN = 0; + NO_SIDE_EFFECTS = 1; + IDEMPOTENT = 2; + } + extensions 1000 to max; +} +message OneofDescriptorProto { + optional string name = 1; + optional OneofOptions options = 2; +} +message OneofOptions { + optional FeatureSet features = 1; + repeated UninterpretedOption uninterpreted_option = 999; + extensions 1000 to max; +} +message ServiceDescriptorProto { + optional string name = 1; + repeated MethodDescriptorProto method = 2; + optional ServiceOptions options = 3; +} +message ServiceOptions { + optional bool deprecated = 33 [default = false]; + optional FeatureSet features = 34; + repeated UninterpretedOption uninterpreted_option = 999; + extensions 1000 to max; +} +message SourceCodeInfo { + repeated Location location = 1; + message Location { + repeated int32 path = 1 [packed = true]; + repeated int32 span = 2 [packed = true]; + optional string leading_comments = 3; + optional string trailing_comments = 4; + repeated string leading_detached_comments = 6; + } + extensions 536000000 [ + declaration = { + number: 536000000, + full_name: ".buf.descriptor.v1.buf_source_code_info_extension", + type: ".buf.descriptor.v1.SourceCodeInfoExtension" + } + ]; +} +message UninterpretedOption { + repeated NamePart name = 2; + optional string identifier_value = 3; + optional uint64 positive_int_value = 4; + optional int64 negative_int_value = 5; + optional double double_value = 6; + optional bytes string_value = 7; + optional string aggregate_value = 8; + message NamePart { + required string name_part = 1; + required bool is_extension = 2; + } +} +enum Edition { + EDITION_UNKNOWN = 0; + EDITION_1_TEST_ONLY = 1; + EDITION_2_TEST_ONLY = 2; + EDITION_LEGACY = 900; + EDITION_PROTO2 = 998; + EDITION_PROTO3 = 999; + EDITION_2023 = 1000; + EDITION_2024 = 1001; + EDITION_99997_TEST_ONLY = 99997; + EDITION_99998_TEST_ONLY = 99998; + EDITION_99999_TEST_ONLY = 99999; + EDITION_MAX = 2147483647; +} +-- options.proto -- +syntax = "proto3"; +import "google/protobuf/descriptor.proto"; +message Files { + google.protobuf.FileDescriptorSet files = 1; +} +message UnusedOption { + string foo = 1; +} +message UsedOption { + string foo = 1; + extend google.protobuf.FileOptions { + UsedOption file_foo = 50000; + UnusedOption file_bar = 50001; + string file_baz = 50002; + } +} +extend google.protobuf.EnumOptions { + UsedOption enum_foo = 50000; + UnusedOption enum_bar = 50001; + string enum_baz = 50002; +} +extend google.protobuf.EnumValueOptions { + UsedOption enum_value_foo = 50000; + UnusedOption enum_value_bar = 50001; + string enum_value_baz = 50002; +} +extend google.protobuf.FieldOptions { + UsedOption field_foo = 50000; + UnusedOption field_bar = 50001; + string field_baz = 50002; +} +extend google.protobuf.MessageOptions { + UsedOption message_foo = 50000; + UnusedOption message_bar = 50001; + string message_baz = 50002; +} +extend google.protobuf.MethodOptions { + UsedOption method_foo = 50000; + UnusedOption method_bar = 50001; + string method_baz = 50002; +} +extend google.protobuf.OneofOptions { + UsedOption oneof_foo = 50000; + UnusedOption oneof_bar = 50001; + string oneof_baz = 50002; +} +extend google.protobuf.ServiceOptions { + UsedOption service_foo = 50000; + UnusedOption service_bar = 50001; + string service_baz = 50002; +} diff --git a/private/bufpkg/bufimage/bufimageutil/testdata/options/options.foo.exclude.txtar b/private/bufpkg/bufimage/bufimageutil/testdata/options/options.foo.exclude.txtar new file mode 100644 index 0000000000..92c977db6b --- /dev/null +++ b/private/bufpkg/bufimage/bufimageutil/testdata/options/options.foo.exclude.txtar @@ -0,0 +1,540 @@ +-- a.proto -- +syntax = "proto2"; +package pkg; +import "options.proto"; +option (UsedOption.file_baz) = "str"; +message Empty { +} +message Foo { + option (message_baz) = "str"; + optional uint64 foo = 1 [jstype = JS_STRING, (field_baz) = "str"]; + oneof testOneof { + option (oneof_baz) = "str"; + string bar = 2; + bytes baz = 3; + } + extensions 10 to max; +} +enum FooEnum { + option deprecated = true; + option (enum_baz) = "str"; + FOO_ENUM_X = 0; + FOO_ENUM_Y = 1 [(enum_value_baz) = "str"]; +} +service FooService { + option (service_baz) = "str"; + rpc Do ( Empty ) returns ( Empty ) { + option (method_baz) = "str"; + } + rpc DoNot ( Empty ) returns ( Empty ) { + option (method_baz) = "str"; + } +} +extend Foo { + optional string extension = 11 [(field_baz) = "str"]; +} +-- google/protobuf/descriptor.proto -- +syntax = "proto2"; +package google.protobuf; +option cc_enable_arenas = true; +option csharp_namespace = "Google.Protobuf.Reflection"; +option go_package = "google.golang.org/protobuf/types/descriptorpb"; +option java_outer_classname = "DescriptorProtos"; +option java_package = "com.google.protobuf"; +option objc_class_prefix = "GPB"; +option optimize_for = SPEED; +message DescriptorProto { + optional string name = 1; + repeated FieldDescriptorProto field = 2; + repeated DescriptorProto nested_type = 3; + repeated EnumDescriptorProto enum_type = 4; + repeated ExtensionRange extension_range = 5; + repeated FieldDescriptorProto extension = 6; + optional MessageOptions options = 7; + repeated OneofDescriptorProto oneof_decl = 8; + repeated ReservedRange reserved_range = 9; + repeated string reserved_name = 10; + message ExtensionRange { + optional int32 start = 1; + optional int32 end = 2; + optional ExtensionRangeOptions options = 3; + } + message ReservedRange { + optional int32 start = 1; + optional int32 end = 2; + } +} +message EnumDescriptorProto { + optional string name = 1; + repeated EnumValueDescriptorProto value = 2; + optional EnumOptions options = 3; + repeated EnumReservedRange reserved_range = 4; + repeated string reserved_name = 5; + message EnumReservedRange { + optional int32 start = 1; + optional int32 end = 2; + } +} +message EnumOptions { + optional bool allow_alias = 2; + optional bool deprecated = 3 [default = false]; + optional bool deprecated_legacy_json_field_conflicts = 6 [deprecated = true]; + optional FeatureSet features = 7; + repeated UninterpretedOption uninterpreted_option = 999; + extensions 1000 to max; + reserved 5; +} +message EnumValueDescriptorProto { + optional string name = 1; + optional int32 number = 2; + optional EnumValueOptions options = 3; +} +message EnumValueOptions { + optional bool deprecated = 1 [default = false]; + optional FeatureSet features = 2; + optional bool debug_redact = 3 [default = false]; + optional FieldOptions.FeatureSupport feature_support = 4; + repeated UninterpretedOption uninterpreted_option = 999; + extensions 1000 to max; +} +message ExtensionRangeOptions { + repeated Declaration declaration = 2 [retention = RETENTION_SOURCE]; + optional VerificationState verification = 3 [default = UNVERIFIED, retention = RETENTION_SOURCE]; + optional FeatureSet features = 50; + repeated UninterpretedOption uninterpreted_option = 999; + message Declaration { + optional int32 number = 1; + optional string full_name = 2; + optional string type = 3; + optional bool reserved = 5; + optional bool repeated = 6; + reserved 4; + } + enum VerificationState { + DECLARATION = 0; + UNVERIFIED = 1; + } + extensions 1000 to max; +} +message FeatureSet { + optional FieldPresence field_presence = 1 [ + edition_defaults = { value: "EXPLICIT", edition: EDITION_LEGACY }, + edition_defaults = { value: "IMPLICIT", edition: EDITION_PROTO3 }, + edition_defaults = { value: "EXPLICIT", edition: EDITION_2023 }, + feature_support = { edition_introduced: EDITION_2023 }, + retention = RETENTION_RUNTIME, + targets = TARGET_TYPE_FIELD, + targets = TARGET_TYPE_FILE + ]; + optional EnumType enum_type = 2 [ + edition_defaults = { value: "CLOSED", edition: EDITION_LEGACY }, + edition_defaults = { value: "OPEN", edition: EDITION_PROTO3 }, + feature_support = { edition_introduced: EDITION_2023 }, + retention = RETENTION_RUNTIME, + targets = TARGET_TYPE_ENUM, + targets = TARGET_TYPE_FILE + ]; + optional RepeatedFieldEncoding repeated_field_encoding = 3 [ + edition_defaults = { value: "EXPANDED", edition: EDITION_LEGACY }, + edition_defaults = { value: "PACKED", edition: EDITION_PROTO3 }, + feature_support = { edition_introduced: EDITION_2023 }, + retention = RETENTION_RUNTIME, + targets = TARGET_TYPE_FIELD, + targets = TARGET_TYPE_FILE + ]; + optional Utf8Validation utf8_validation = 4 [ + edition_defaults = { value: "NONE", edition: EDITION_LEGACY }, + edition_defaults = { value: "VERIFY", edition: EDITION_PROTO3 }, + feature_support = { edition_introduced: EDITION_2023 }, + retention = RETENTION_RUNTIME, + targets = TARGET_TYPE_FIELD, + targets = TARGET_TYPE_FILE + ]; + optional MessageEncoding message_encoding = 5 [ + edition_defaults = { value: "LENGTH_PREFIXED", edition: EDITION_LEGACY }, + feature_support = { edition_introduced: EDITION_2023 }, + retention = RETENTION_RUNTIME, + targets = TARGET_TYPE_FIELD, + targets = TARGET_TYPE_FILE + ]; + optional JsonFormat json_format = 6 [ + edition_defaults = { value: "LEGACY_BEST_EFFORT", edition: EDITION_LEGACY }, + edition_defaults = { value: "ALLOW", edition: EDITION_PROTO3 }, + feature_support = { edition_introduced: EDITION_2023 }, + retention = RETENTION_RUNTIME, + targets = TARGET_TYPE_MESSAGE, + targets = TARGET_TYPE_ENUM, + targets = TARGET_TYPE_FILE + ]; + enum EnumType { + ENUM_TYPE_UNKNOWN = 0; + OPEN = 1; + CLOSED = 2; + } + enum FieldPresence { + FIELD_PRESENCE_UNKNOWN = 0; + EXPLICIT = 1; + IMPLICIT = 2; + LEGACY_REQUIRED = 3; + } + enum JsonFormat { + JSON_FORMAT_UNKNOWN = 0; + ALLOW = 1; + LEGACY_BEST_EFFORT = 2; + } + enum MessageEncoding { + MESSAGE_ENCODING_UNKNOWN = 0; + LENGTH_PREFIXED = 1; + DELIMITED = 2; + } + enum RepeatedFieldEncoding { + REPEATED_FIELD_ENCODING_UNKNOWN = 0; + PACKED = 1; + EXPANDED = 2; + } + enum Utf8Validation { + UTF8_VALIDATION_UNKNOWN = 0; + VERIFY = 2; + NONE = 3; + reserved 1; + } + extensions 1000 to 9994 [ + declaration = { + number: 1000, + full_name: ".pb.cpp", + type: ".pb.CppFeatures" + }, + declaration = { + number: 1001, + full_name: ".pb.java", + type: ".pb.JavaFeatures" + }, + declaration = { + number: 1002, + full_name: ".pb.go", + type: ".pb.GoFeatures" + }, + declaration = { + number: 9990, + full_name: ".pb.proto1", + type: ".pb.Proto1Features" + } + ]; + extensions 9995 to 9999, 10000; + reserved 999; +} +message FeatureSetDefaults { + repeated FeatureSetEditionDefault defaults = 1; + optional Edition minimum_edition = 4; + optional Edition maximum_edition = 5; + message FeatureSetEditionDefault { + optional Edition edition = 3; + optional FeatureSet overridable_features = 4; + optional FeatureSet fixed_features = 5; + reserved 1, 2; + reserved "features"; + } +} +message FieldDescriptorProto { + optional string name = 1; + optional string extendee = 2; + optional int32 number = 3; + optional Label label = 4; + optional Type type = 5; + optional string type_name = 6; + optional string default_value = 7; + optional FieldOptions options = 8; + optional int32 oneof_index = 9; + optional string json_name = 10; + optional bool proto3_optional = 17; + enum Label { + LABEL_OPTIONAL = 1; + LABEL_REQUIRED = 2; + LABEL_REPEATED = 3; + } + enum Type { + TYPE_DOUBLE = 1; + TYPE_FLOAT = 2; + TYPE_INT64 = 3; + TYPE_UINT64 = 4; + TYPE_INT32 = 5; + TYPE_FIXED64 = 6; + TYPE_FIXED32 = 7; + TYPE_BOOL = 8; + TYPE_STRING = 9; + TYPE_GROUP = 10; + TYPE_MESSAGE = 11; + TYPE_BYTES = 12; + TYPE_UINT32 = 13; + TYPE_ENUM = 14; + TYPE_SFIXED32 = 15; + TYPE_SFIXED64 = 16; + TYPE_SINT32 = 17; + TYPE_SINT64 = 18; + } +} +message FieldOptions { + optional CType ctype = 1 [default = STRING]; + optional bool packed = 2; + optional bool deprecated = 3 [default = false]; + optional bool lazy = 5 [default = false]; + optional JSType jstype = 6 [default = JS_NORMAL]; + optional bool weak = 10 [default = false]; + optional bool unverified_lazy = 15 [default = false]; + optional bool debug_redact = 16 [default = false]; + optional OptionRetention retention = 17; + repeated OptionTargetType targets = 19; + repeated EditionDefault edition_defaults = 20; + optional FeatureSet features = 21; + optional FeatureSupport feature_support = 22; + repeated UninterpretedOption uninterpreted_option = 999; + message EditionDefault { + optional string value = 2; + optional Edition edition = 3; + } + message FeatureSupport { + optional Edition edition_introduced = 1; + optional Edition edition_deprecated = 2; + optional string deprecation_warning = 3; + optional Edition edition_removed = 4; + } + enum CType { + STRING = 0; + CORD = 1; + STRING_PIECE = 2; + } + enum JSType { + JS_NORMAL = 0; + JS_STRING = 1; + JS_NUMBER = 2; + } + enum OptionRetention { + RETENTION_UNKNOWN = 0; + RETENTION_RUNTIME = 1; + RETENTION_SOURCE = 2; + } + enum OptionTargetType { + TARGET_TYPE_UNKNOWN = 0; + TARGET_TYPE_FILE = 1; + TARGET_TYPE_EXTENSION_RANGE = 2; + TARGET_TYPE_MESSAGE = 3; + TARGET_TYPE_FIELD = 4; + TARGET_TYPE_ONEOF = 5; + TARGET_TYPE_ENUM = 6; + TARGET_TYPE_ENUM_ENTRY = 7; + TARGET_TYPE_SERVICE = 8; + TARGET_TYPE_METHOD = 9; + } + extensions 1000 to max; + reserved 4, 18; +} +message FileDescriptorProto { + optional string name = 1; + optional string package = 2; + repeated string dependency = 3; + repeated DescriptorProto message_type = 4; + repeated EnumDescriptorProto enum_type = 5; + repeated ServiceDescriptorProto service = 6; + repeated FieldDescriptorProto extension = 7; + optional FileOptions options = 8; + optional SourceCodeInfo source_code_info = 9; + repeated int32 public_dependency = 10; + repeated int32 weak_dependency = 11; + optional string syntax = 12; + optional Edition edition = 14; +} +message FileDescriptorSet { + repeated FileDescriptorProto file = 1; + extensions 536000000 [ + declaration = { + number: 536000000, + full_name: ".buf.descriptor.v1.buf_file_descriptor_set_extension", + type: ".buf.descriptor.v1.FileDescriptorSetExtension" + } + ]; +} +message FileOptions { + optional string java_package = 1; + optional string java_outer_classname = 8; + optional OptimizeMode optimize_for = 9 [default = SPEED]; + optional bool java_multiple_files = 10 [default = false]; + optional string go_package = 11; + optional bool cc_generic_services = 16 [default = false]; + optional bool java_generic_services = 17 [default = false]; + optional bool py_generic_services = 18 [default = false]; + optional bool java_generate_equals_and_hash = 20 [deprecated = true]; + optional bool deprecated = 23 [default = false]; + optional bool java_string_check_utf8 = 27 [default = false]; + optional bool cc_enable_arenas = 31 [default = true]; + optional string objc_class_prefix = 36; + optional string csharp_namespace = 37; + optional string swift_prefix = 39; + optional string php_class_prefix = 40; + optional string php_namespace = 41; + optional string php_metadata_namespace = 44; + optional string ruby_package = 45; + optional FeatureSet features = 50; + repeated UninterpretedOption uninterpreted_option = 999; + enum OptimizeMode { + SPEED = 1; + CODE_SIZE = 2; + LITE_RUNTIME = 3; + } + extensions 1000 to max; + reserved 38, 42; + reserved "php_generic_services"; +} +message GeneratedCodeInfo { + repeated Annotation annotation = 1; + message Annotation { + repeated int32 path = 1 [packed = true]; + optional string source_file = 2; + optional int32 begin = 3; + optional int32 end = 4; + optional Semantic semantic = 5; + enum Semantic { + NONE = 0; + SET = 1; + ALIAS = 2; + } + } +} +message MessageOptions { + optional bool message_set_wire_format = 1 [default = false]; + optional bool no_standard_descriptor_accessor = 2 [default = false]; + optional bool deprecated = 3 [default = false]; + optional bool map_entry = 7; + optional bool deprecated_legacy_json_field_conflicts = 11 [deprecated = true]; + optional FeatureSet features = 12; + repeated UninterpretedOption uninterpreted_option = 999; + extensions 1000 to max; + reserved 4, 5, 6, 8, 9; +} +message MethodDescriptorProto { + optional string name = 1; + optional string input_type = 2; + optional string output_type = 3; + optional MethodOptions options = 4; + optional bool client_streaming = 5 [default = false]; + optional bool server_streaming = 6 [default = false]; +} +message MethodOptions { + optional bool deprecated = 33 [default = false]; + optional IdempotencyLevel idempotency_level = 34 [default = IDEMPOTENCY_UNKNOWN]; + optional FeatureSet features = 35; + repeated UninterpretedOption uninterpreted_option = 999; + enum IdempotencyLevel { + IDEMPOTENCY_UNKNOWN = 0; + NO_SIDE_EFFECTS = 1; + IDEMPOTENT = 2; + } + extensions 1000 to max; +} +message OneofDescriptorProto { + optional string name = 1; + optional OneofOptions options = 2; +} +message OneofOptions { + optional FeatureSet features = 1; + repeated UninterpretedOption uninterpreted_option = 999; + extensions 1000 to max; +} +message ServiceDescriptorProto { + optional string name = 1; + repeated MethodDescriptorProto method = 2; + optional ServiceOptions options = 3; +} +message ServiceOptions { + optional bool deprecated = 33 [default = false]; + optional FeatureSet features = 34; + repeated UninterpretedOption uninterpreted_option = 999; + extensions 1000 to max; +} +message SourceCodeInfo { + repeated Location location = 1; + message Location { + repeated int32 path = 1 [packed = true]; + repeated int32 span = 2 [packed = true]; + optional string leading_comments = 3; + optional string trailing_comments = 4; + repeated string leading_detached_comments = 6; + } + extensions 536000000 [ + declaration = { + number: 536000000, + full_name: ".buf.descriptor.v1.buf_source_code_info_extension", + type: ".buf.descriptor.v1.SourceCodeInfoExtension" + } + ]; +} +message UninterpretedOption { + repeated NamePart name = 2; + optional string identifier_value = 3; + optional uint64 positive_int_value = 4; + optional int64 negative_int_value = 5; + optional double double_value = 6; + optional bytes string_value = 7; + optional string aggregate_value = 8; + message NamePart { + required string name_part = 1; + required bool is_extension = 2; + } +} +enum Edition { + EDITION_UNKNOWN = 0; + EDITION_1_TEST_ONLY = 1; + EDITION_2_TEST_ONLY = 2; + EDITION_LEGACY = 900; + EDITION_PROTO2 = 998; + EDITION_PROTO3 = 999; + EDITION_2023 = 1000; + EDITION_2024 = 1001; + EDITION_99997_TEST_ONLY = 99997; + EDITION_99998_TEST_ONLY = 99998; + EDITION_99999_TEST_ONLY = 99999; + EDITION_MAX = 2147483647; +} +-- options.proto -- +syntax = "proto3"; +import "google/protobuf/descriptor.proto"; +message Files { + google.protobuf.FileDescriptorSet files = 1; +} +message UnusedOption { + string foo = 1; +} +message UsedOption { + string foo = 1; + extend google.protobuf.FileOptions { + UnusedOption file_bar = 50001; + string file_baz = 50002; + } +} +extend google.protobuf.EnumOptions { + UnusedOption enum_bar = 50001; + string enum_baz = 50002; +} +extend google.protobuf.EnumValueOptions { + UnusedOption enum_value_bar = 50001; + string enum_value_baz = 50002; +} +extend google.protobuf.FieldOptions { + UnusedOption field_bar = 50001; + string field_baz = 50002; +} +extend google.protobuf.MessageOptions { + UnusedOption message_bar = 50001; + string message_baz = 50002; +} +extend google.protobuf.MethodOptions { + UnusedOption method_bar = 50001; + string method_baz = 50002; +} +extend google.protobuf.OneofOptions { + UnusedOption oneof_bar = 50001; + string oneof_baz = 50002; +} +extend google.protobuf.ServiceOptions { + UnusedOption service_bar = 50001; + string service_baz = 50002; +} diff --git a/private/bufpkg/bufimage/bufimageutil/testdata/options/options.foo.include.txtar b/private/bufpkg/bufimage/bufimageutil/testdata/options/options.foo.include.txtar new file mode 100644 index 0000000000..8d6c038c76 --- /dev/null +++ b/private/bufpkg/bufimage/bufimageutil/testdata/options/options.foo.include.txtar @@ -0,0 +1,330 @@ +-- google/protobuf/descriptor.proto -- +syntax = "proto2"; +package google.protobuf; +option cc_enable_arenas = true; +option csharp_namespace = "Google.Protobuf.Reflection"; +option go_package = "google.golang.org/protobuf/types/descriptorpb"; +option java_outer_classname = "DescriptorProtos"; +option java_package = "com.google.protobuf"; +option objc_class_prefix = "GPB"; +option optimize_for = SPEED; +message EnumOptions { + optional bool allow_alias = 2; + optional bool deprecated = 3 [default = false]; + optional bool deprecated_legacy_json_field_conflicts = 6 [deprecated = true]; + optional FeatureSet features = 7; + repeated UninterpretedOption uninterpreted_option = 999; + extensions 1000 to max; + reserved 5; +} +message EnumValueOptions { + optional bool deprecated = 1 [default = false]; + optional FeatureSet features = 2; + optional bool debug_redact = 3 [default = false]; + optional FieldOptions.FeatureSupport feature_support = 4; + repeated UninterpretedOption uninterpreted_option = 999; + extensions 1000 to max; +} +message FeatureSet { + optional FieldPresence field_presence = 1 [ + edition_defaults = { value: "EXPLICIT", edition: EDITION_LEGACY }, + edition_defaults = { value: "IMPLICIT", edition: EDITION_PROTO3 }, + edition_defaults = { value: "EXPLICIT", edition: EDITION_2023 }, + feature_support = { edition_introduced: EDITION_2023 }, + retention = RETENTION_RUNTIME, + targets = TARGET_TYPE_FIELD, + targets = TARGET_TYPE_FILE + ]; + optional EnumType enum_type = 2 [ + edition_defaults = { value: "CLOSED", edition: EDITION_LEGACY }, + edition_defaults = { value: "OPEN", edition: EDITION_PROTO3 }, + feature_support = { edition_introduced: EDITION_2023 }, + retention = RETENTION_RUNTIME, + targets = TARGET_TYPE_ENUM, + targets = TARGET_TYPE_FILE + ]; + optional RepeatedFieldEncoding repeated_field_encoding = 3 [ + edition_defaults = { value: "EXPANDED", edition: EDITION_LEGACY }, + edition_defaults = { value: "PACKED", edition: EDITION_PROTO3 }, + feature_support = { edition_introduced: EDITION_2023 }, + retention = RETENTION_RUNTIME, + targets = TARGET_TYPE_FIELD, + targets = TARGET_TYPE_FILE + ]; + optional Utf8Validation utf8_validation = 4 [ + edition_defaults = { value: "NONE", edition: EDITION_LEGACY }, + edition_defaults = { value: "VERIFY", edition: EDITION_PROTO3 }, + feature_support = { edition_introduced: EDITION_2023 }, + retention = RETENTION_RUNTIME, + targets = TARGET_TYPE_FIELD, + targets = TARGET_TYPE_FILE + ]; + optional MessageEncoding message_encoding = 5 [ + edition_defaults = { value: "LENGTH_PREFIXED", edition: EDITION_LEGACY }, + feature_support = { edition_introduced: EDITION_2023 }, + retention = RETENTION_RUNTIME, + targets = TARGET_TYPE_FIELD, + targets = TARGET_TYPE_FILE + ]; + optional JsonFormat json_format = 6 [ + edition_defaults = { value: "LEGACY_BEST_EFFORT", edition: EDITION_LEGACY }, + edition_defaults = { value: "ALLOW", edition: EDITION_PROTO3 }, + feature_support = { edition_introduced: EDITION_2023 }, + retention = RETENTION_RUNTIME, + targets = TARGET_TYPE_MESSAGE, + targets = TARGET_TYPE_ENUM, + targets = TARGET_TYPE_FILE + ]; + enum EnumType { + ENUM_TYPE_UNKNOWN = 0; + OPEN = 1; + CLOSED = 2; + } + enum FieldPresence { + FIELD_PRESENCE_UNKNOWN = 0; + EXPLICIT = 1; + IMPLICIT = 2; + LEGACY_REQUIRED = 3; + } + enum JsonFormat { + JSON_FORMAT_UNKNOWN = 0; + ALLOW = 1; + LEGACY_BEST_EFFORT = 2; + } + enum MessageEncoding { + MESSAGE_ENCODING_UNKNOWN = 0; + LENGTH_PREFIXED = 1; + DELIMITED = 2; + } + enum RepeatedFieldEncoding { + REPEATED_FIELD_ENCODING_UNKNOWN = 0; + PACKED = 1; + EXPANDED = 2; + } + enum Utf8Validation { + UTF8_VALIDATION_UNKNOWN = 0; + VERIFY = 2; + NONE = 3; + reserved 1; + } + extensions 1000 to 9994 [ + declaration = { + number: 1000, + full_name: ".pb.cpp", + type: ".pb.CppFeatures" + }, + declaration = { + number: 1001, + full_name: ".pb.java", + type: ".pb.JavaFeatures" + }, + declaration = { + number: 1002, + full_name: ".pb.go", + type: ".pb.GoFeatures" + }, + declaration = { + number: 9990, + full_name: ".pb.proto1", + type: ".pb.Proto1Features" + } + ]; + extensions 9995 to 9999, 10000; + reserved 999; +} +message FieldOptions { + optional CType ctype = 1 [default = STRING]; + optional bool packed = 2; + optional bool deprecated = 3 [default = false]; + optional bool lazy = 5 [default = false]; + optional JSType jstype = 6 [default = JS_NORMAL]; + optional bool weak = 10 [default = false]; + optional bool unverified_lazy = 15 [default = false]; + optional bool debug_redact = 16 [default = false]; + optional OptionRetention retention = 17; + repeated OptionTargetType targets = 19; + repeated EditionDefault edition_defaults = 20; + optional FeatureSet features = 21; + optional FeatureSupport feature_support = 22; + repeated UninterpretedOption uninterpreted_option = 999; + message EditionDefault { + optional string value = 2; + optional Edition edition = 3; + } + message FeatureSupport { + optional Edition edition_introduced = 1; + optional Edition edition_deprecated = 2; + optional string deprecation_warning = 3; + optional Edition edition_removed = 4; + } + enum CType { + STRING = 0; + CORD = 1; + STRING_PIECE = 2; + } + enum JSType { + JS_NORMAL = 0; + JS_STRING = 1; + JS_NUMBER = 2; + } + enum OptionRetention { + RETENTION_UNKNOWN = 0; + RETENTION_RUNTIME = 1; + RETENTION_SOURCE = 2; + } + enum OptionTargetType { + TARGET_TYPE_UNKNOWN = 0; + TARGET_TYPE_FILE = 1; + TARGET_TYPE_EXTENSION_RANGE = 2; + TARGET_TYPE_MESSAGE = 3; + TARGET_TYPE_FIELD = 4; + TARGET_TYPE_ONEOF = 5; + TARGET_TYPE_ENUM = 6; + TARGET_TYPE_ENUM_ENTRY = 7; + TARGET_TYPE_SERVICE = 8; + TARGET_TYPE_METHOD = 9; + } + extensions 1000 to max; + reserved 4, 18; +} +message FileOptions { + optional string java_package = 1; + optional string java_outer_classname = 8; + optional OptimizeMode optimize_for = 9 [default = SPEED]; + optional bool java_multiple_files = 10 [default = false]; + optional string go_package = 11; + optional bool cc_generic_services = 16 [default = false]; + optional bool java_generic_services = 17 [default = false]; + optional bool py_generic_services = 18 [default = false]; + optional bool java_generate_equals_and_hash = 20 [deprecated = true]; + optional bool deprecated = 23 [default = false]; + optional bool java_string_check_utf8 = 27 [default = false]; + optional bool cc_enable_arenas = 31 [default = true]; + optional string objc_class_prefix = 36; + optional string csharp_namespace = 37; + optional string swift_prefix = 39; + optional string php_class_prefix = 40; + optional string php_namespace = 41; + optional string php_metadata_namespace = 44; + optional string ruby_package = 45; + optional FeatureSet features = 50; + repeated UninterpretedOption uninterpreted_option = 999; + enum OptimizeMode { + SPEED = 1; + CODE_SIZE = 2; + LITE_RUNTIME = 3; + } + extensions 1000 to max; + reserved 38, 42; + reserved "php_generic_services"; +} +message MessageOptions { + optional bool message_set_wire_format = 1 [default = false]; + optional bool no_standard_descriptor_accessor = 2 [default = false]; + optional bool deprecated = 3 [default = false]; + optional bool map_entry = 7; + optional bool deprecated_legacy_json_field_conflicts = 11 [deprecated = true]; + optional FeatureSet features = 12; + repeated UninterpretedOption uninterpreted_option = 999; + extensions 1000 to max; + reserved 4, 5, 6, 8, 9; +} +message MethodOptions { + optional bool deprecated = 33 [default = false]; + optional IdempotencyLevel idempotency_level = 34 [default = IDEMPOTENCY_UNKNOWN]; + optional FeatureSet features = 35; + repeated UninterpretedOption uninterpreted_option = 999; + enum IdempotencyLevel { + IDEMPOTENCY_UNKNOWN = 0; + NO_SIDE_EFFECTS = 1; + IDEMPOTENT = 2; + } + extensions 1000 to max; +} +message OneofOptions { + optional FeatureSet features = 1; + repeated UninterpretedOption uninterpreted_option = 999; + extensions 1000 to max; +} +message ServiceOptions { + optional bool deprecated = 33 [default = false]; + optional FeatureSet features = 34; + repeated UninterpretedOption uninterpreted_option = 999; + extensions 1000 to max; +} +message UninterpretedOption { + repeated NamePart name = 2; + optional string identifier_value = 3; + optional uint64 positive_int_value = 4; + optional int64 negative_int_value = 5; + optional double double_value = 6; + optional bytes string_value = 7; + optional string aggregate_value = 8; + message NamePart { + required string name_part = 1; + required bool is_extension = 2; + } +} +enum Edition { + EDITION_UNKNOWN = 0; + EDITION_1_TEST_ONLY = 1; + EDITION_2_TEST_ONLY = 2; + EDITION_LEGACY = 900; + EDITION_PROTO2 = 998; + EDITION_PROTO3 = 999; + EDITION_2023 = 1000; + EDITION_2024 = 1001; + EDITION_99997_TEST_ONLY = 99997; + EDITION_99998_TEST_ONLY = 99998; + EDITION_99999_TEST_ONLY = 99999; + EDITION_MAX = 2147483647; +} +-- options.proto -- +syntax = "proto3"; +import "google/protobuf/descriptor.proto"; +message UnusedOption { + string foo = 1; +} +message UsedOption { + string foo = 1; + extend google.protobuf.FileOptions { + UsedOption file_foo = 50000; + UnusedOption file_bar = 50001; + string file_baz = 50002; + } +} +extend google.protobuf.EnumOptions { + UsedOption enum_foo = 50000; + UnusedOption enum_bar = 50001; + string enum_baz = 50002; +} +extend google.protobuf.EnumValueOptions { + UsedOption enum_value_foo = 50000; + UnusedOption enum_value_bar = 50001; + string enum_value_baz = 50002; +} +extend google.protobuf.FieldOptions { + UsedOption field_foo = 50000; + UnusedOption field_bar = 50001; + string field_baz = 50002; +} +extend google.protobuf.MessageOptions { + UsedOption message_foo = 50000; + UnusedOption message_bar = 50001; + string message_baz = 50002; +} +extend google.protobuf.MethodOptions { + UsedOption method_foo = 50000; + UnusedOption method_bar = 50001; + string method_baz = 50002; +} +extend google.protobuf.OneofOptions { + UsedOption oneof_foo = 50000; + UnusedOption oneof_bar = 50001; + string oneof_baz = 50002; +} +extend google.protobuf.ServiceOptions { + UsedOption service_foo = 50000; + UnusedOption service_bar = 50001; + string service_baz = 50002; +} diff --git a/private/bufpkg/bufimage/bufimageutil/testdata/options/options.only_file.txtar b/private/bufpkg/bufimage/bufimageutil/testdata/options/options.only_file.txtar new file mode 100644 index 0000000000..9b02187490 --- /dev/null +++ b/private/bufpkg/bufimage/bufimageutil/testdata/options/options.only_file.txtar @@ -0,0 +1,506 @@ +-- a.proto -- +syntax = "proto2"; +package pkg; +import "options.proto"; +option (UsedOption.file_baz) = "str"; +option (UsedOption.file_foo) = { foo: "str" }; +message Empty { +} +message Foo { + optional uint64 foo = 1 [jstype = JS_STRING]; + oneof testOneof { + string bar = 2; + bytes baz = 3; + } + extensions 10 to max; +} +enum FooEnum { + option deprecated = true; + FOO_ENUM_X = 0; + FOO_ENUM_Y = 1; +} +service FooService { + rpc Do ( Empty ) returns ( Empty ); + rpc DoNot ( Empty ) returns ( Empty ); +} +extend Foo { + optional string extension = 11; +} +-- google/protobuf/descriptor.proto -- +syntax = "proto2"; +package google.protobuf; +option cc_enable_arenas = true; +option csharp_namespace = "Google.Protobuf.Reflection"; +option go_package = "google.golang.org/protobuf/types/descriptorpb"; +option java_outer_classname = "DescriptorProtos"; +option java_package = "com.google.protobuf"; +option objc_class_prefix = "GPB"; +option optimize_for = SPEED; +message DescriptorProto { + optional string name = 1; + repeated FieldDescriptorProto field = 2; + repeated DescriptorProto nested_type = 3; + repeated EnumDescriptorProto enum_type = 4; + repeated ExtensionRange extension_range = 5; + repeated FieldDescriptorProto extension = 6; + optional MessageOptions options = 7; + repeated OneofDescriptorProto oneof_decl = 8; + repeated ReservedRange reserved_range = 9; + repeated string reserved_name = 10; + message ExtensionRange { + optional int32 start = 1; + optional int32 end = 2; + optional ExtensionRangeOptions options = 3; + } + message ReservedRange { + optional int32 start = 1; + optional int32 end = 2; + } +} +message EnumDescriptorProto { + optional string name = 1; + repeated EnumValueDescriptorProto value = 2; + optional EnumOptions options = 3; + repeated EnumReservedRange reserved_range = 4; + repeated string reserved_name = 5; + message EnumReservedRange { + optional int32 start = 1; + optional int32 end = 2; + } +} +message EnumOptions { + optional bool allow_alias = 2; + optional bool deprecated = 3 [default = false]; + optional bool deprecated_legacy_json_field_conflicts = 6 [deprecated = true]; + optional FeatureSet features = 7; + repeated UninterpretedOption uninterpreted_option = 999; + extensions 1000 to max; + reserved 5; +} +message EnumValueDescriptorProto { + optional string name = 1; + optional int32 number = 2; + optional EnumValueOptions options = 3; +} +message EnumValueOptions { + optional bool deprecated = 1 [default = false]; + optional FeatureSet features = 2; + optional bool debug_redact = 3 [default = false]; + optional FieldOptions.FeatureSupport feature_support = 4; + repeated UninterpretedOption uninterpreted_option = 999; + extensions 1000 to max; +} +message ExtensionRangeOptions { + repeated Declaration declaration = 2 [retention = RETENTION_SOURCE]; + optional VerificationState verification = 3 [default = UNVERIFIED, retention = RETENTION_SOURCE]; + optional FeatureSet features = 50; + repeated UninterpretedOption uninterpreted_option = 999; + message Declaration { + optional int32 number = 1; + optional string full_name = 2; + optional string type = 3; + optional bool reserved = 5; + optional bool repeated = 6; + reserved 4; + } + enum VerificationState { + DECLARATION = 0; + UNVERIFIED = 1; + } + extensions 1000 to max; +} +message FeatureSet { + optional FieldPresence field_presence = 1 [ + edition_defaults = { value: "EXPLICIT", edition: EDITION_LEGACY }, + edition_defaults = { value: "IMPLICIT", edition: EDITION_PROTO3 }, + edition_defaults = { value: "EXPLICIT", edition: EDITION_2023 }, + feature_support = { edition_introduced: EDITION_2023 }, + retention = RETENTION_RUNTIME, + targets = TARGET_TYPE_FIELD, + targets = TARGET_TYPE_FILE + ]; + optional EnumType enum_type = 2 [ + edition_defaults = { value: "CLOSED", edition: EDITION_LEGACY }, + edition_defaults = { value: "OPEN", edition: EDITION_PROTO3 }, + feature_support = { edition_introduced: EDITION_2023 }, + retention = RETENTION_RUNTIME, + targets = TARGET_TYPE_ENUM, + targets = TARGET_TYPE_FILE + ]; + optional RepeatedFieldEncoding repeated_field_encoding = 3 [ + edition_defaults = { value: "EXPANDED", edition: EDITION_LEGACY }, + edition_defaults = { value: "PACKED", edition: EDITION_PROTO3 }, + feature_support = { edition_introduced: EDITION_2023 }, + retention = RETENTION_RUNTIME, + targets = TARGET_TYPE_FIELD, + targets = TARGET_TYPE_FILE + ]; + optional Utf8Validation utf8_validation = 4 [ + edition_defaults = { value: "NONE", edition: EDITION_LEGACY }, + edition_defaults = { value: "VERIFY", edition: EDITION_PROTO3 }, + feature_support = { edition_introduced: EDITION_2023 }, + retention = RETENTION_RUNTIME, + targets = TARGET_TYPE_FIELD, + targets = TARGET_TYPE_FILE + ]; + optional MessageEncoding message_encoding = 5 [ + edition_defaults = { value: "LENGTH_PREFIXED", edition: EDITION_LEGACY }, + feature_support = { edition_introduced: EDITION_2023 }, + retention = RETENTION_RUNTIME, + targets = TARGET_TYPE_FIELD, + targets = TARGET_TYPE_FILE + ]; + optional JsonFormat json_format = 6 [ + edition_defaults = { value: "LEGACY_BEST_EFFORT", edition: EDITION_LEGACY }, + edition_defaults = { value: "ALLOW", edition: EDITION_PROTO3 }, + feature_support = { edition_introduced: EDITION_2023 }, + retention = RETENTION_RUNTIME, + targets = TARGET_TYPE_MESSAGE, + targets = TARGET_TYPE_ENUM, + targets = TARGET_TYPE_FILE + ]; + enum EnumType { + ENUM_TYPE_UNKNOWN = 0; + OPEN = 1; + CLOSED = 2; + } + enum FieldPresence { + FIELD_PRESENCE_UNKNOWN = 0; + EXPLICIT = 1; + IMPLICIT = 2; + LEGACY_REQUIRED = 3; + } + enum JsonFormat { + JSON_FORMAT_UNKNOWN = 0; + ALLOW = 1; + LEGACY_BEST_EFFORT = 2; + } + enum MessageEncoding { + MESSAGE_ENCODING_UNKNOWN = 0; + LENGTH_PREFIXED = 1; + DELIMITED = 2; + } + enum RepeatedFieldEncoding { + REPEATED_FIELD_ENCODING_UNKNOWN = 0; + PACKED = 1; + EXPANDED = 2; + } + enum Utf8Validation { + UTF8_VALIDATION_UNKNOWN = 0; + VERIFY = 2; + NONE = 3; + reserved 1; + } + extensions 1000 to 9994 [ + declaration = { + number: 1000, + full_name: ".pb.cpp", + type: ".pb.CppFeatures" + }, + declaration = { + number: 1001, + full_name: ".pb.java", + type: ".pb.JavaFeatures" + }, + declaration = { + number: 1002, + full_name: ".pb.go", + type: ".pb.GoFeatures" + }, + declaration = { + number: 9990, + full_name: ".pb.proto1", + type: ".pb.Proto1Features" + } + ]; + extensions 9995 to 9999, 10000; + reserved 999; +} +message FeatureSetDefaults { + repeated FeatureSetEditionDefault defaults = 1; + optional Edition minimum_edition = 4; + optional Edition maximum_edition = 5; + message FeatureSetEditionDefault { + optional Edition edition = 3; + optional FeatureSet overridable_features = 4; + optional FeatureSet fixed_features = 5; + reserved 1, 2; + reserved "features"; + } +} +message FieldDescriptorProto { + optional string name = 1; + optional string extendee = 2; + optional int32 number = 3; + optional Label label = 4; + optional Type type = 5; + optional string type_name = 6; + optional string default_value = 7; + optional FieldOptions options = 8; + optional int32 oneof_index = 9; + optional string json_name = 10; + optional bool proto3_optional = 17; + enum Label { + LABEL_OPTIONAL = 1; + LABEL_REQUIRED = 2; + LABEL_REPEATED = 3; + } + enum Type { + TYPE_DOUBLE = 1; + TYPE_FLOAT = 2; + TYPE_INT64 = 3; + TYPE_UINT64 = 4; + TYPE_INT32 = 5; + TYPE_FIXED64 = 6; + TYPE_FIXED32 = 7; + TYPE_BOOL = 8; + TYPE_STRING = 9; + TYPE_GROUP = 10; + TYPE_MESSAGE = 11; + TYPE_BYTES = 12; + TYPE_UINT32 = 13; + TYPE_ENUM = 14; + TYPE_SFIXED32 = 15; + TYPE_SFIXED64 = 16; + TYPE_SINT32 = 17; + TYPE_SINT64 = 18; + } +} +message FieldOptions { + optional CType ctype = 1 [default = STRING]; + optional bool packed = 2; + optional bool deprecated = 3 [default = false]; + optional bool lazy = 5 [default = false]; + optional JSType jstype = 6 [default = JS_NORMAL]; + optional bool weak = 10 [default = false]; + optional bool unverified_lazy = 15 [default = false]; + optional bool debug_redact = 16 [default = false]; + optional OptionRetention retention = 17; + repeated OptionTargetType targets = 19; + repeated EditionDefault edition_defaults = 20; + optional FeatureSet features = 21; + optional FeatureSupport feature_support = 22; + repeated UninterpretedOption uninterpreted_option = 999; + message EditionDefault { + optional string value = 2; + optional Edition edition = 3; + } + message FeatureSupport { + optional Edition edition_introduced = 1; + optional Edition edition_deprecated = 2; + optional string deprecation_warning = 3; + optional Edition edition_removed = 4; + } + enum CType { + STRING = 0; + CORD = 1; + STRING_PIECE = 2; + } + enum JSType { + JS_NORMAL = 0; + JS_STRING = 1; + JS_NUMBER = 2; + } + enum OptionRetention { + RETENTION_UNKNOWN = 0; + RETENTION_RUNTIME = 1; + RETENTION_SOURCE = 2; + } + enum OptionTargetType { + TARGET_TYPE_UNKNOWN = 0; + TARGET_TYPE_FILE = 1; + TARGET_TYPE_EXTENSION_RANGE = 2; + TARGET_TYPE_MESSAGE = 3; + TARGET_TYPE_FIELD = 4; + TARGET_TYPE_ONEOF = 5; + TARGET_TYPE_ENUM = 6; + TARGET_TYPE_ENUM_ENTRY = 7; + TARGET_TYPE_SERVICE = 8; + TARGET_TYPE_METHOD = 9; + } + extensions 1000 to max; + reserved 4, 18; +} +message FileDescriptorProto { + optional string name = 1; + optional string package = 2; + repeated string dependency = 3; + repeated DescriptorProto message_type = 4; + repeated EnumDescriptorProto enum_type = 5; + repeated ServiceDescriptorProto service = 6; + repeated FieldDescriptorProto extension = 7; + optional FileOptions options = 8; + optional SourceCodeInfo source_code_info = 9; + repeated int32 public_dependency = 10; + repeated int32 weak_dependency = 11; + optional string syntax = 12; + optional Edition edition = 14; +} +message FileDescriptorSet { + repeated FileDescriptorProto file = 1; + extensions 536000000 [ + declaration = { + number: 536000000, + full_name: ".buf.descriptor.v1.buf_file_descriptor_set_extension", + type: ".buf.descriptor.v1.FileDescriptorSetExtension" + } + ]; +} +message FileOptions { + optional string java_package = 1; + optional string java_outer_classname = 8; + optional OptimizeMode optimize_for = 9 [default = SPEED]; + optional bool java_multiple_files = 10 [default = false]; + optional string go_package = 11; + optional bool cc_generic_services = 16 [default = false]; + optional bool java_generic_services = 17 [default = false]; + optional bool py_generic_services = 18 [default = false]; + optional bool java_generate_equals_and_hash = 20 [deprecated = true]; + optional bool deprecated = 23 [default = false]; + optional bool java_string_check_utf8 = 27 [default = false]; + optional bool cc_enable_arenas = 31 [default = true]; + optional string objc_class_prefix = 36; + optional string csharp_namespace = 37; + optional string swift_prefix = 39; + optional string php_class_prefix = 40; + optional string php_namespace = 41; + optional string php_metadata_namespace = 44; + optional string ruby_package = 45; + optional FeatureSet features = 50; + repeated UninterpretedOption uninterpreted_option = 999; + enum OptimizeMode { + SPEED = 1; + CODE_SIZE = 2; + LITE_RUNTIME = 3; + } + extensions 1000 to max; + reserved 38, 42; + reserved "php_generic_services"; +} +message GeneratedCodeInfo { + repeated Annotation annotation = 1; + message Annotation { + repeated int32 path = 1 [packed = true]; + optional string source_file = 2; + optional int32 begin = 3; + optional int32 end = 4; + optional Semantic semantic = 5; + enum Semantic { + NONE = 0; + SET = 1; + ALIAS = 2; + } + } +} +message MessageOptions { + optional bool message_set_wire_format = 1 [default = false]; + optional bool no_standard_descriptor_accessor = 2 [default = false]; + optional bool deprecated = 3 [default = false]; + optional bool map_entry = 7; + optional bool deprecated_legacy_json_field_conflicts = 11 [deprecated = true]; + optional FeatureSet features = 12; + repeated UninterpretedOption uninterpreted_option = 999; + extensions 1000 to max; + reserved 4, 5, 6, 8, 9; +} +message MethodDescriptorProto { + optional string name = 1; + optional string input_type = 2; + optional string output_type = 3; + optional MethodOptions options = 4; + optional bool client_streaming = 5 [default = false]; + optional bool server_streaming = 6 [default = false]; +} +message MethodOptions { + optional bool deprecated = 33 [default = false]; + optional IdempotencyLevel idempotency_level = 34 [default = IDEMPOTENCY_UNKNOWN]; + optional FeatureSet features = 35; + repeated UninterpretedOption uninterpreted_option = 999; + enum IdempotencyLevel { + IDEMPOTENCY_UNKNOWN = 0; + NO_SIDE_EFFECTS = 1; + IDEMPOTENT = 2; + } + extensions 1000 to max; +} +message OneofDescriptorProto { + optional string name = 1; + optional OneofOptions options = 2; +} +message OneofOptions { + optional FeatureSet features = 1; + repeated UninterpretedOption uninterpreted_option = 999; + extensions 1000 to max; +} +message ServiceDescriptorProto { + optional string name = 1; + repeated MethodDescriptorProto method = 2; + optional ServiceOptions options = 3; +} +message ServiceOptions { + optional bool deprecated = 33 [default = false]; + optional FeatureSet features = 34; + repeated UninterpretedOption uninterpreted_option = 999; + extensions 1000 to max; +} +message SourceCodeInfo { + repeated Location location = 1; + message Location { + repeated int32 path = 1 [packed = true]; + repeated int32 span = 2 [packed = true]; + optional string leading_comments = 3; + optional string trailing_comments = 4; + repeated string leading_detached_comments = 6; + } + extensions 536000000 [ + declaration = { + number: 536000000, + full_name: ".buf.descriptor.v1.buf_source_code_info_extension", + type: ".buf.descriptor.v1.SourceCodeInfoExtension" + } + ]; +} +message UninterpretedOption { + repeated NamePart name = 2; + optional string identifier_value = 3; + optional uint64 positive_int_value = 4; + optional int64 negative_int_value = 5; + optional double double_value = 6; + optional bytes string_value = 7; + optional string aggregate_value = 8; + message NamePart { + required string name_part = 1; + required bool is_extension = 2; + } +} +enum Edition { + EDITION_UNKNOWN = 0; + EDITION_1_TEST_ONLY = 1; + EDITION_2_TEST_ONLY = 2; + EDITION_LEGACY = 900; + EDITION_PROTO2 = 998; + EDITION_PROTO3 = 999; + EDITION_2023 = 1000; + EDITION_2024 = 1001; + EDITION_99997_TEST_ONLY = 99997; + EDITION_99998_TEST_ONLY = 99998; + EDITION_99999_TEST_ONLY = 99999; + EDITION_MAX = 2147483647; +} +-- options.proto -- +syntax = "proto3"; +import "google/protobuf/descriptor.proto"; +message Files { + google.protobuf.FileDescriptorSet files = 1; +} +message UnusedOption { + string foo = 1; +} +message UsedOption { + string foo = 1; + extend google.protobuf.FileOptions { + UsedOption file_foo = 50000; + UnusedOption file_bar = 50001; + string file_baz = 50002; + } +} diff --git a/private/bufpkg/bufimage/bufimageutil/testdata/options/pkg.Foo.exclude.txtar b/private/bufpkg/bufimage/bufimageutil/testdata/options/pkg.Foo.exclude.txtar new file mode 100644 index 0000000000..65a44f4ff0 --- /dev/null +++ b/private/bufpkg/bufimage/bufimageutil/testdata/options/pkg.Foo.exclude.txtar @@ -0,0 +1,543 @@ +-- a.proto -- +syntax = "proto2"; +package pkg; +import "options.proto"; +option (UsedOption.file_baz) = "str"; +option (UsedOption.file_foo) = { foo: "str" }; +message Empty { +} +enum FooEnum { + option deprecated = true; + option (enum_baz) = "str"; + option (enum_foo) = { foo: "str" }; + FOO_ENUM_X = 0; + FOO_ENUM_Y = 1 [ + (enum_value_baz) = "str", + (enum_value_foo) = { foo: "str" } + ]; +} +service FooService { + option (service_baz) = "str"; + option (service_foo) = { foo: "str" }; + rpc Do ( Empty ) returns ( Empty ) { + option (method_baz) = "str"; + option (method_foo) = { foo: "str" }; + } + rpc DoNot ( Empty ) returns ( Empty ) { + option (method_baz) = "str"; + option (method_foo) = { foo: "str" }; + } +} +-- google/protobuf/descriptor.proto -- +syntax = "proto2"; +package google.protobuf; +option cc_enable_arenas = true; +option csharp_namespace = "Google.Protobuf.Reflection"; +option go_package = "google.golang.org/protobuf/types/descriptorpb"; +option java_outer_classname = "DescriptorProtos"; +option java_package = "com.google.protobuf"; +option objc_class_prefix = "GPB"; +option optimize_for = SPEED; +message DescriptorProto { + optional string name = 1; + repeated FieldDescriptorProto field = 2; + repeated DescriptorProto nested_type = 3; + repeated EnumDescriptorProto enum_type = 4; + repeated ExtensionRange extension_range = 5; + repeated FieldDescriptorProto extension = 6; + optional MessageOptions options = 7; + repeated OneofDescriptorProto oneof_decl = 8; + repeated ReservedRange reserved_range = 9; + repeated string reserved_name = 10; + message ExtensionRange { + optional int32 start = 1; + optional int32 end = 2; + optional ExtensionRangeOptions options = 3; + } + message ReservedRange { + optional int32 start = 1; + optional int32 end = 2; + } +} +message EnumDescriptorProto { + optional string name = 1; + repeated EnumValueDescriptorProto value = 2; + optional EnumOptions options = 3; + repeated EnumReservedRange reserved_range = 4; + repeated string reserved_name = 5; + message EnumReservedRange { + optional int32 start = 1; + optional int32 end = 2; + } +} +message EnumOptions { + optional bool allow_alias = 2; + optional bool deprecated = 3 [default = false]; + optional bool deprecated_legacy_json_field_conflicts = 6 [deprecated = true]; + optional FeatureSet features = 7; + repeated UninterpretedOption uninterpreted_option = 999; + extensions 1000 to max; + reserved 5; +} +message EnumValueDescriptorProto { + optional string name = 1; + optional int32 number = 2; + optional EnumValueOptions options = 3; +} +message EnumValueOptions { + optional bool deprecated = 1 [default = false]; + optional FeatureSet features = 2; + optional bool debug_redact = 3 [default = false]; + optional FieldOptions.FeatureSupport feature_support = 4; + repeated UninterpretedOption uninterpreted_option = 999; + extensions 1000 to max; +} +message ExtensionRangeOptions { + repeated Declaration declaration = 2 [retention = RETENTION_SOURCE]; + optional VerificationState verification = 3 [default = UNVERIFIED, retention = RETENTION_SOURCE]; + optional FeatureSet features = 50; + repeated UninterpretedOption uninterpreted_option = 999; + message Declaration { + optional int32 number = 1; + optional string full_name = 2; + optional string type = 3; + optional bool reserved = 5; + optional bool repeated = 6; + reserved 4; + } + enum VerificationState { + DECLARATION = 0; + UNVERIFIED = 1; + } + extensions 1000 to max; +} +message FeatureSet { + optional FieldPresence field_presence = 1 [ + edition_defaults = { value: "EXPLICIT", edition: EDITION_LEGACY }, + edition_defaults = { value: "IMPLICIT", edition: EDITION_PROTO3 }, + edition_defaults = { value: "EXPLICIT", edition: EDITION_2023 }, + feature_support = { edition_introduced: EDITION_2023 }, + retention = RETENTION_RUNTIME, + targets = TARGET_TYPE_FIELD, + targets = TARGET_TYPE_FILE + ]; + optional EnumType enum_type = 2 [ + edition_defaults = { value: "CLOSED", edition: EDITION_LEGACY }, + edition_defaults = { value: "OPEN", edition: EDITION_PROTO3 }, + feature_support = { edition_introduced: EDITION_2023 }, + retention = RETENTION_RUNTIME, + targets = TARGET_TYPE_ENUM, + targets = TARGET_TYPE_FILE + ]; + optional RepeatedFieldEncoding repeated_field_encoding = 3 [ + edition_defaults = { value: "EXPANDED", edition: EDITION_LEGACY }, + edition_defaults = { value: "PACKED", edition: EDITION_PROTO3 }, + feature_support = { edition_introduced: EDITION_2023 }, + retention = RETENTION_RUNTIME, + targets = TARGET_TYPE_FIELD, + targets = TARGET_TYPE_FILE + ]; + optional Utf8Validation utf8_validation = 4 [ + edition_defaults = { value: "NONE", edition: EDITION_LEGACY }, + edition_defaults = { value: "VERIFY", edition: EDITION_PROTO3 }, + feature_support = { edition_introduced: EDITION_2023 }, + retention = RETENTION_RUNTIME, + targets = TARGET_TYPE_FIELD, + targets = TARGET_TYPE_FILE + ]; + optional MessageEncoding message_encoding = 5 [ + edition_defaults = { value: "LENGTH_PREFIXED", edition: EDITION_LEGACY }, + feature_support = { edition_introduced: EDITION_2023 }, + retention = RETENTION_RUNTIME, + targets = TARGET_TYPE_FIELD, + targets = TARGET_TYPE_FILE + ]; + optional JsonFormat json_format = 6 [ + edition_defaults = { value: "LEGACY_BEST_EFFORT", edition: EDITION_LEGACY }, + edition_defaults = { value: "ALLOW", edition: EDITION_PROTO3 }, + feature_support = { edition_introduced: EDITION_2023 }, + retention = RETENTION_RUNTIME, + targets = TARGET_TYPE_MESSAGE, + targets = TARGET_TYPE_ENUM, + targets = TARGET_TYPE_FILE + ]; + enum EnumType { + ENUM_TYPE_UNKNOWN = 0; + OPEN = 1; + CLOSED = 2; + } + enum FieldPresence { + FIELD_PRESENCE_UNKNOWN = 0; + EXPLICIT = 1; + IMPLICIT = 2; + LEGACY_REQUIRED = 3; + } + enum JsonFormat { + JSON_FORMAT_UNKNOWN = 0; + ALLOW = 1; + LEGACY_BEST_EFFORT = 2; + } + enum MessageEncoding { + MESSAGE_ENCODING_UNKNOWN = 0; + LENGTH_PREFIXED = 1; + DELIMITED = 2; + } + enum RepeatedFieldEncoding { + REPEATED_FIELD_ENCODING_UNKNOWN = 0; + PACKED = 1; + EXPANDED = 2; + } + enum Utf8Validation { + UTF8_VALIDATION_UNKNOWN = 0; + VERIFY = 2; + NONE = 3; + reserved 1; + } + extensions 1000 to 9994 [ + declaration = { + number: 1000, + full_name: ".pb.cpp", + type: ".pb.CppFeatures" + }, + declaration = { + number: 1001, + full_name: ".pb.java", + type: ".pb.JavaFeatures" + }, + declaration = { + number: 1002, + full_name: ".pb.go", + type: ".pb.GoFeatures" + }, + declaration = { + number: 9990, + full_name: ".pb.proto1", + type: ".pb.Proto1Features" + } + ]; + extensions 9995 to 9999, 10000; + reserved 999; +} +message FeatureSetDefaults { + repeated FeatureSetEditionDefault defaults = 1; + optional Edition minimum_edition = 4; + optional Edition maximum_edition = 5; + message FeatureSetEditionDefault { + optional Edition edition = 3; + optional FeatureSet overridable_features = 4; + optional FeatureSet fixed_features = 5; + reserved 1, 2; + reserved "features"; + } +} +message FieldDescriptorProto { + optional string name = 1; + optional string extendee = 2; + optional int32 number = 3; + optional Label label = 4; + optional Type type = 5; + optional string type_name = 6; + optional string default_value = 7; + optional FieldOptions options = 8; + optional int32 oneof_index = 9; + optional string json_name = 10; + optional bool proto3_optional = 17; + enum Label { + LABEL_OPTIONAL = 1; + LABEL_REQUIRED = 2; + LABEL_REPEATED = 3; + } + enum Type { + TYPE_DOUBLE = 1; + TYPE_FLOAT = 2; + TYPE_INT64 = 3; + TYPE_UINT64 = 4; + TYPE_INT32 = 5; + TYPE_FIXED64 = 6; + TYPE_FIXED32 = 7; + TYPE_BOOL = 8; + TYPE_STRING = 9; + TYPE_GROUP = 10; + TYPE_MESSAGE = 11; + TYPE_BYTES = 12; + TYPE_UINT32 = 13; + TYPE_ENUM = 14; + TYPE_SFIXED32 = 15; + TYPE_SFIXED64 = 16; + TYPE_SINT32 = 17; + TYPE_SINT64 = 18; + } +} +message FieldOptions { + optional CType ctype = 1 [default = STRING]; + optional bool packed = 2; + optional bool deprecated = 3 [default = false]; + optional bool lazy = 5 [default = false]; + optional JSType jstype = 6 [default = JS_NORMAL]; + optional bool weak = 10 [default = false]; + optional bool unverified_lazy = 15 [default = false]; + optional bool debug_redact = 16 [default = false]; + optional OptionRetention retention = 17; + repeated OptionTargetType targets = 19; + repeated EditionDefault edition_defaults = 20; + optional FeatureSet features = 21; + optional FeatureSupport feature_support = 22; + repeated UninterpretedOption uninterpreted_option = 999; + message EditionDefault { + optional string value = 2; + optional Edition edition = 3; + } + message FeatureSupport { + optional Edition edition_introduced = 1; + optional Edition edition_deprecated = 2; + optional string deprecation_warning = 3; + optional Edition edition_removed = 4; + } + enum CType { + STRING = 0; + CORD = 1; + STRING_PIECE = 2; + } + enum JSType { + JS_NORMAL = 0; + JS_STRING = 1; + JS_NUMBER = 2; + } + enum OptionRetention { + RETENTION_UNKNOWN = 0; + RETENTION_RUNTIME = 1; + RETENTION_SOURCE = 2; + } + enum OptionTargetType { + TARGET_TYPE_UNKNOWN = 0; + TARGET_TYPE_FILE = 1; + TARGET_TYPE_EXTENSION_RANGE = 2; + TARGET_TYPE_MESSAGE = 3; + TARGET_TYPE_FIELD = 4; + TARGET_TYPE_ONEOF = 5; + TARGET_TYPE_ENUM = 6; + TARGET_TYPE_ENUM_ENTRY = 7; + TARGET_TYPE_SERVICE = 8; + TARGET_TYPE_METHOD = 9; + } + extensions 1000 to max; + reserved 4, 18; +} +message FileDescriptorProto { + optional string name = 1; + optional string package = 2; + repeated string dependency = 3; + repeated DescriptorProto message_type = 4; + repeated EnumDescriptorProto enum_type = 5; + repeated ServiceDescriptorProto service = 6; + repeated FieldDescriptorProto extension = 7; + optional FileOptions options = 8; + optional SourceCodeInfo source_code_info = 9; + repeated int32 public_dependency = 10; + repeated int32 weak_dependency = 11; + optional string syntax = 12; + optional Edition edition = 14; +} +message FileDescriptorSet { + repeated FileDescriptorProto file = 1; + extensions 536000000 [ + declaration = { + number: 536000000, + full_name: ".buf.descriptor.v1.buf_file_descriptor_set_extension", + type: ".buf.descriptor.v1.FileDescriptorSetExtension" + } + ]; +} +message FileOptions { + optional string java_package = 1; + optional string java_outer_classname = 8; + optional OptimizeMode optimize_for = 9 [default = SPEED]; + optional bool java_multiple_files = 10 [default = false]; + optional string go_package = 11; + optional bool cc_generic_services = 16 [default = false]; + optional bool java_generic_services = 17 [default = false]; + optional bool py_generic_services = 18 [default = false]; + optional bool java_generate_equals_and_hash = 20 [deprecated = true]; + optional bool deprecated = 23 [default = false]; + optional bool java_string_check_utf8 = 27 [default = false]; + optional bool cc_enable_arenas = 31 [default = true]; + optional string objc_class_prefix = 36; + optional string csharp_namespace = 37; + optional string swift_prefix = 39; + optional string php_class_prefix = 40; + optional string php_namespace = 41; + optional string php_metadata_namespace = 44; + optional string ruby_package = 45; + optional FeatureSet features = 50; + repeated UninterpretedOption uninterpreted_option = 999; + enum OptimizeMode { + SPEED = 1; + CODE_SIZE = 2; + LITE_RUNTIME = 3; + } + extensions 1000 to max; + reserved 38, 42; + reserved "php_generic_services"; +} +message GeneratedCodeInfo { + repeated Annotation annotation = 1; + message Annotation { + repeated int32 path = 1 [packed = true]; + optional string source_file = 2; + optional int32 begin = 3; + optional int32 end = 4; + optional Semantic semantic = 5; + enum Semantic { + NONE = 0; + SET = 1; + ALIAS = 2; + } + } +} +message MessageOptions { + optional bool message_set_wire_format = 1 [default = false]; + optional bool no_standard_descriptor_accessor = 2 [default = false]; + optional bool deprecated = 3 [default = false]; + optional bool map_entry = 7; + optional bool deprecated_legacy_json_field_conflicts = 11 [deprecated = true]; + optional FeatureSet features = 12; + repeated UninterpretedOption uninterpreted_option = 999; + extensions 1000 to max; + reserved 4, 5, 6, 8, 9; +} +message MethodDescriptorProto { + optional string name = 1; + optional string input_type = 2; + optional string output_type = 3; + optional MethodOptions options = 4; + optional bool client_streaming = 5 [default = false]; + optional bool server_streaming = 6 [default = false]; +} +message MethodOptions { + optional bool deprecated = 33 [default = false]; + optional IdempotencyLevel idempotency_level = 34 [default = IDEMPOTENCY_UNKNOWN]; + optional FeatureSet features = 35; + repeated UninterpretedOption uninterpreted_option = 999; + enum IdempotencyLevel { + IDEMPOTENCY_UNKNOWN = 0; + NO_SIDE_EFFECTS = 1; + IDEMPOTENT = 2; + } + extensions 1000 to max; +} +message OneofDescriptorProto { + optional string name = 1; + optional OneofOptions options = 2; +} +message OneofOptions { + optional FeatureSet features = 1; + repeated UninterpretedOption uninterpreted_option = 999; + extensions 1000 to max; +} +message ServiceDescriptorProto { + optional string name = 1; + repeated MethodDescriptorProto method = 2; + optional ServiceOptions options = 3; +} +message ServiceOptions { + optional bool deprecated = 33 [default = false]; + optional FeatureSet features = 34; + repeated UninterpretedOption uninterpreted_option = 999; + extensions 1000 to max; +} +message SourceCodeInfo { + repeated Location location = 1; + message Location { + repeated int32 path = 1 [packed = true]; + repeated int32 span = 2 [packed = true]; + optional string leading_comments = 3; + optional string trailing_comments = 4; + repeated string leading_detached_comments = 6; + } + extensions 536000000 [ + declaration = { + number: 536000000, + full_name: ".buf.descriptor.v1.buf_source_code_info_extension", + type: ".buf.descriptor.v1.SourceCodeInfoExtension" + } + ]; +} +message UninterpretedOption { + repeated NamePart name = 2; + optional string identifier_value = 3; + optional uint64 positive_int_value = 4; + optional int64 negative_int_value = 5; + optional double double_value = 6; + optional bytes string_value = 7; + optional string aggregate_value = 8; + message NamePart { + required string name_part = 1; + required bool is_extension = 2; + } +} +enum Edition { + EDITION_UNKNOWN = 0; + EDITION_1_TEST_ONLY = 1; + EDITION_2_TEST_ONLY = 2; + EDITION_LEGACY = 900; + EDITION_PROTO2 = 998; + EDITION_PROTO3 = 999; + EDITION_2023 = 1000; + EDITION_2024 = 1001; + EDITION_99997_TEST_ONLY = 99997; + EDITION_99998_TEST_ONLY = 99998; + EDITION_99999_TEST_ONLY = 99999; + EDITION_MAX = 2147483647; +} +-- options.proto -- +syntax = "proto3"; +import "google/protobuf/descriptor.proto"; +message Files { + google.protobuf.FileDescriptorSet files = 1; +} +message UnusedOption { + string foo = 1; +} +message UsedOption { + string foo = 1; + extend google.protobuf.FileOptions { + UsedOption file_foo = 50000; + UnusedOption file_bar = 50001; + string file_baz = 50002; + } +} +extend google.protobuf.EnumOptions { + UsedOption enum_foo = 50000; + UnusedOption enum_bar = 50001; + string enum_baz = 50002; +} +extend google.protobuf.EnumValueOptions { + UsedOption enum_value_foo = 50000; + UnusedOption enum_value_bar = 50001; + string enum_value_baz = 50002; +} +extend google.protobuf.FieldOptions { + UsedOption field_foo = 50000; + UnusedOption field_bar = 50001; + string field_baz = 50002; +} +extend google.protobuf.MessageOptions { + UsedOption message_foo = 50000; + UnusedOption message_bar = 50001; + string message_baz = 50002; +} +extend google.protobuf.MethodOptions { + UsedOption method_foo = 50000; + UnusedOption method_bar = 50001; + string method_baz = 50002; +} +extend google.protobuf.OneofOptions { + UsedOption oneof_foo = 50000; + UnusedOption oneof_bar = 50001; + string oneof_baz = 50002; +} +extend google.protobuf.ServiceOptions { + UsedOption service_foo = 50000; + UnusedOption service_bar = 50001; + string service_baz = 50002; +} diff --git a/private/bufpkg/bufimage/bufimageutil/testdata/options/pkg.FooEnum.exclude.txtar b/private/bufpkg/bufimage/bufimageutil/testdata/options/pkg.FooEnum.exclude.txtar new file mode 100644 index 0000000000..89385adeed --- /dev/null +++ b/private/bufpkg/bufimage/bufimageutil/testdata/options/pkg.FooEnum.exclude.txtar @@ -0,0 +1,552 @@ +-- a.proto -- +syntax = "proto2"; +package pkg; +import "options.proto"; +option (UsedOption.file_baz) = "str"; +option (UsedOption.file_foo) = { foo: "str" }; +message Empty { +} +message Foo { + option (message_baz) = "str"; + option (message_foo) = { foo: "str" }; + optional uint64 foo = 1 [ + jstype = JS_STRING, + (field_baz) = "str", + (field_foo) = { foo: "str" } + ]; + oneof testOneof { + option (oneof_baz) = "str"; + option (oneof_foo) = { foo: "str" }; + string bar = 2; + bytes baz = 3; + } + extensions 10 to max; +} +service FooService { + option (service_baz) = "str"; + option (service_foo) = { foo: "str" }; + rpc Do ( Empty ) returns ( Empty ) { + option (method_baz) = "str"; + option (method_foo) = { foo: "str" }; + } + rpc DoNot ( Empty ) returns ( Empty ) { + option (method_baz) = "str"; + option (method_foo) = { foo: "str" }; + } +} +extend Foo { + optional string extension = 11 [(field_baz) = "str", (field_foo) = { foo: "str" }]; +} +-- google/protobuf/descriptor.proto -- +syntax = "proto2"; +package google.protobuf; +option cc_enable_arenas = true; +option csharp_namespace = "Google.Protobuf.Reflection"; +option go_package = "google.golang.org/protobuf/types/descriptorpb"; +option java_outer_classname = "DescriptorProtos"; +option java_package = "com.google.protobuf"; +option objc_class_prefix = "GPB"; +option optimize_for = SPEED; +message DescriptorProto { + optional string name = 1; + repeated FieldDescriptorProto field = 2; + repeated DescriptorProto nested_type = 3; + repeated EnumDescriptorProto enum_type = 4; + repeated ExtensionRange extension_range = 5; + repeated FieldDescriptorProto extension = 6; + optional MessageOptions options = 7; + repeated OneofDescriptorProto oneof_decl = 8; + repeated ReservedRange reserved_range = 9; + repeated string reserved_name = 10; + message ExtensionRange { + optional int32 start = 1; + optional int32 end = 2; + optional ExtensionRangeOptions options = 3; + } + message ReservedRange { + optional int32 start = 1; + optional int32 end = 2; + } +} +message EnumDescriptorProto { + optional string name = 1; + repeated EnumValueDescriptorProto value = 2; + optional EnumOptions options = 3; + repeated EnumReservedRange reserved_range = 4; + repeated string reserved_name = 5; + message EnumReservedRange { + optional int32 start = 1; + optional int32 end = 2; + } +} +message EnumOptions { + optional bool allow_alias = 2; + optional bool deprecated = 3 [default = false]; + optional bool deprecated_legacy_json_field_conflicts = 6 [deprecated = true]; + optional FeatureSet features = 7; + repeated UninterpretedOption uninterpreted_option = 999; + extensions 1000 to max; + reserved 5; +} +message EnumValueDescriptorProto { + optional string name = 1; + optional int32 number = 2; + optional EnumValueOptions options = 3; +} +message EnumValueOptions { + optional bool deprecated = 1 [default = false]; + optional FeatureSet features = 2; + optional bool debug_redact = 3 [default = false]; + optional FieldOptions.FeatureSupport feature_support = 4; + repeated UninterpretedOption uninterpreted_option = 999; + extensions 1000 to max; +} +message ExtensionRangeOptions { + repeated Declaration declaration = 2 [retention = RETENTION_SOURCE]; + optional VerificationState verification = 3 [default = UNVERIFIED, retention = RETENTION_SOURCE]; + optional FeatureSet features = 50; + repeated UninterpretedOption uninterpreted_option = 999; + message Declaration { + optional int32 number = 1; + optional string full_name = 2; + optional string type = 3; + optional bool reserved = 5; + optional bool repeated = 6; + reserved 4; + } + enum VerificationState { + DECLARATION = 0; + UNVERIFIED = 1; + } + extensions 1000 to max; +} +message FeatureSet { + optional FieldPresence field_presence = 1 [ + edition_defaults = { value: "EXPLICIT", edition: EDITION_LEGACY }, + edition_defaults = { value: "IMPLICIT", edition: EDITION_PROTO3 }, + edition_defaults = { value: "EXPLICIT", edition: EDITION_2023 }, + feature_support = { edition_introduced: EDITION_2023 }, + retention = RETENTION_RUNTIME, + targets = TARGET_TYPE_FIELD, + targets = TARGET_TYPE_FILE + ]; + optional EnumType enum_type = 2 [ + edition_defaults = { value: "CLOSED", edition: EDITION_LEGACY }, + edition_defaults = { value: "OPEN", edition: EDITION_PROTO3 }, + feature_support = { edition_introduced: EDITION_2023 }, + retention = RETENTION_RUNTIME, + targets = TARGET_TYPE_ENUM, + targets = TARGET_TYPE_FILE + ]; + optional RepeatedFieldEncoding repeated_field_encoding = 3 [ + edition_defaults = { value: "EXPANDED", edition: EDITION_LEGACY }, + edition_defaults = { value: "PACKED", edition: EDITION_PROTO3 }, + feature_support = { edition_introduced: EDITION_2023 }, + retention = RETENTION_RUNTIME, + targets = TARGET_TYPE_FIELD, + targets = TARGET_TYPE_FILE + ]; + optional Utf8Validation utf8_validation = 4 [ + edition_defaults = { value: "NONE", edition: EDITION_LEGACY }, + edition_defaults = { value: "VERIFY", edition: EDITION_PROTO3 }, + feature_support = { edition_introduced: EDITION_2023 }, + retention = RETENTION_RUNTIME, + targets = TARGET_TYPE_FIELD, + targets = TARGET_TYPE_FILE + ]; + optional MessageEncoding message_encoding = 5 [ + edition_defaults = { value: "LENGTH_PREFIXED", edition: EDITION_LEGACY }, + feature_support = { edition_introduced: EDITION_2023 }, + retention = RETENTION_RUNTIME, + targets = TARGET_TYPE_FIELD, + targets = TARGET_TYPE_FILE + ]; + optional JsonFormat json_format = 6 [ + edition_defaults = { value: "LEGACY_BEST_EFFORT", edition: EDITION_LEGACY }, + edition_defaults = { value: "ALLOW", edition: EDITION_PROTO3 }, + feature_support = { edition_introduced: EDITION_2023 }, + retention = RETENTION_RUNTIME, + targets = TARGET_TYPE_MESSAGE, + targets = TARGET_TYPE_ENUM, + targets = TARGET_TYPE_FILE + ]; + enum EnumType { + ENUM_TYPE_UNKNOWN = 0; + OPEN = 1; + CLOSED = 2; + } + enum FieldPresence { + FIELD_PRESENCE_UNKNOWN = 0; + EXPLICIT = 1; + IMPLICIT = 2; + LEGACY_REQUIRED = 3; + } + enum JsonFormat { + JSON_FORMAT_UNKNOWN = 0; + ALLOW = 1; + LEGACY_BEST_EFFORT = 2; + } + enum MessageEncoding { + MESSAGE_ENCODING_UNKNOWN = 0; + LENGTH_PREFIXED = 1; + DELIMITED = 2; + } + enum RepeatedFieldEncoding { + REPEATED_FIELD_ENCODING_UNKNOWN = 0; + PACKED = 1; + EXPANDED = 2; + } + enum Utf8Validation { + UTF8_VALIDATION_UNKNOWN = 0; + VERIFY = 2; + NONE = 3; + reserved 1; + } + extensions 1000 to 9994 [ + declaration = { + number: 1000, + full_name: ".pb.cpp", + type: ".pb.CppFeatures" + }, + declaration = { + number: 1001, + full_name: ".pb.java", + type: ".pb.JavaFeatures" + }, + declaration = { + number: 1002, + full_name: ".pb.go", + type: ".pb.GoFeatures" + }, + declaration = { + number: 9990, + full_name: ".pb.proto1", + type: ".pb.Proto1Features" + } + ]; + extensions 9995 to 9999, 10000; + reserved 999; +} +message FeatureSetDefaults { + repeated FeatureSetEditionDefault defaults = 1; + optional Edition minimum_edition = 4; + optional Edition maximum_edition = 5; + message FeatureSetEditionDefault { + optional Edition edition = 3; + optional FeatureSet overridable_features = 4; + optional FeatureSet fixed_features = 5; + reserved 1, 2; + reserved "features"; + } +} +message FieldDescriptorProto { + optional string name = 1; + optional string extendee = 2; + optional int32 number = 3; + optional Label label = 4; + optional Type type = 5; + optional string type_name = 6; + optional string default_value = 7; + optional FieldOptions options = 8; + optional int32 oneof_index = 9; + optional string json_name = 10; + optional bool proto3_optional = 17; + enum Label { + LABEL_OPTIONAL = 1; + LABEL_REQUIRED = 2; + LABEL_REPEATED = 3; + } + enum Type { + TYPE_DOUBLE = 1; + TYPE_FLOAT = 2; + TYPE_INT64 = 3; + TYPE_UINT64 = 4; + TYPE_INT32 = 5; + TYPE_FIXED64 = 6; + TYPE_FIXED32 = 7; + TYPE_BOOL = 8; + TYPE_STRING = 9; + TYPE_GROUP = 10; + TYPE_MESSAGE = 11; + TYPE_BYTES = 12; + TYPE_UINT32 = 13; + TYPE_ENUM = 14; + TYPE_SFIXED32 = 15; + TYPE_SFIXED64 = 16; + TYPE_SINT32 = 17; + TYPE_SINT64 = 18; + } +} +message FieldOptions { + optional CType ctype = 1 [default = STRING]; + optional bool packed = 2; + optional bool deprecated = 3 [default = false]; + optional bool lazy = 5 [default = false]; + optional JSType jstype = 6 [default = JS_NORMAL]; + optional bool weak = 10 [default = false]; + optional bool unverified_lazy = 15 [default = false]; + optional bool debug_redact = 16 [default = false]; + optional OptionRetention retention = 17; + repeated OptionTargetType targets = 19; + repeated EditionDefault edition_defaults = 20; + optional FeatureSet features = 21; + optional FeatureSupport feature_support = 22; + repeated UninterpretedOption uninterpreted_option = 999; + message EditionDefault { + optional string value = 2; + optional Edition edition = 3; + } + message FeatureSupport { + optional Edition edition_introduced = 1; + optional Edition edition_deprecated = 2; + optional string deprecation_warning = 3; + optional Edition edition_removed = 4; + } + enum CType { + STRING = 0; + CORD = 1; + STRING_PIECE = 2; + } + enum JSType { + JS_NORMAL = 0; + JS_STRING = 1; + JS_NUMBER = 2; + } + enum OptionRetention { + RETENTION_UNKNOWN = 0; + RETENTION_RUNTIME = 1; + RETENTION_SOURCE = 2; + } + enum OptionTargetType { + TARGET_TYPE_UNKNOWN = 0; + TARGET_TYPE_FILE = 1; + TARGET_TYPE_EXTENSION_RANGE = 2; + TARGET_TYPE_MESSAGE = 3; + TARGET_TYPE_FIELD = 4; + TARGET_TYPE_ONEOF = 5; + TARGET_TYPE_ENUM = 6; + TARGET_TYPE_ENUM_ENTRY = 7; + TARGET_TYPE_SERVICE = 8; + TARGET_TYPE_METHOD = 9; + } + extensions 1000 to max; + reserved 4, 18; +} +message FileDescriptorProto { + optional string name = 1; + optional string package = 2; + repeated string dependency = 3; + repeated DescriptorProto message_type = 4; + repeated EnumDescriptorProto enum_type = 5; + repeated ServiceDescriptorProto service = 6; + repeated FieldDescriptorProto extension = 7; + optional FileOptions options = 8; + optional SourceCodeInfo source_code_info = 9; + repeated int32 public_dependency = 10; + repeated int32 weak_dependency = 11; + optional string syntax = 12; + optional Edition edition = 14; +} +message FileDescriptorSet { + repeated FileDescriptorProto file = 1; + extensions 536000000 [ + declaration = { + number: 536000000, + full_name: ".buf.descriptor.v1.buf_file_descriptor_set_extension", + type: ".buf.descriptor.v1.FileDescriptorSetExtension" + } + ]; +} +message FileOptions { + optional string java_package = 1; + optional string java_outer_classname = 8; + optional OptimizeMode optimize_for = 9 [default = SPEED]; + optional bool java_multiple_files = 10 [default = false]; + optional string go_package = 11; + optional bool cc_generic_services = 16 [default = false]; + optional bool java_generic_services = 17 [default = false]; + optional bool py_generic_services = 18 [default = false]; + optional bool java_generate_equals_and_hash = 20 [deprecated = true]; + optional bool deprecated = 23 [default = false]; + optional bool java_string_check_utf8 = 27 [default = false]; + optional bool cc_enable_arenas = 31 [default = true]; + optional string objc_class_prefix = 36; + optional string csharp_namespace = 37; + optional string swift_prefix = 39; + optional string php_class_prefix = 40; + optional string php_namespace = 41; + optional string php_metadata_namespace = 44; + optional string ruby_package = 45; + optional FeatureSet features = 50; + repeated UninterpretedOption uninterpreted_option = 999; + enum OptimizeMode { + SPEED = 1; + CODE_SIZE = 2; + LITE_RUNTIME = 3; + } + extensions 1000 to max; + reserved 38, 42; + reserved "php_generic_services"; +} +message GeneratedCodeInfo { + repeated Annotation annotation = 1; + message Annotation { + repeated int32 path = 1 [packed = true]; + optional string source_file = 2; + optional int32 begin = 3; + optional int32 end = 4; + optional Semantic semantic = 5; + enum Semantic { + NONE = 0; + SET = 1; + ALIAS = 2; + } + } +} +message MessageOptions { + optional bool message_set_wire_format = 1 [default = false]; + optional bool no_standard_descriptor_accessor = 2 [default = false]; + optional bool deprecated = 3 [default = false]; + optional bool map_entry = 7; + optional bool deprecated_legacy_json_field_conflicts = 11 [deprecated = true]; + optional FeatureSet features = 12; + repeated UninterpretedOption uninterpreted_option = 999; + extensions 1000 to max; + reserved 4, 5, 6, 8, 9; +} +message MethodDescriptorProto { + optional string name = 1; + optional string input_type = 2; + optional string output_type = 3; + optional MethodOptions options = 4; + optional bool client_streaming = 5 [default = false]; + optional bool server_streaming = 6 [default = false]; +} +message MethodOptions { + optional bool deprecated = 33 [default = false]; + optional IdempotencyLevel idempotency_level = 34 [default = IDEMPOTENCY_UNKNOWN]; + optional FeatureSet features = 35; + repeated UninterpretedOption uninterpreted_option = 999; + enum IdempotencyLevel { + IDEMPOTENCY_UNKNOWN = 0; + NO_SIDE_EFFECTS = 1; + IDEMPOTENT = 2; + } + extensions 1000 to max; +} +message OneofDescriptorProto { + optional string name = 1; + optional OneofOptions options = 2; +} +message OneofOptions { + optional FeatureSet features = 1; + repeated UninterpretedOption uninterpreted_option = 999; + extensions 1000 to max; +} +message ServiceDescriptorProto { + optional string name = 1; + repeated MethodDescriptorProto method = 2; + optional ServiceOptions options = 3; +} +message ServiceOptions { + optional bool deprecated = 33 [default = false]; + optional FeatureSet features = 34; + repeated UninterpretedOption uninterpreted_option = 999; + extensions 1000 to max; +} +message SourceCodeInfo { + repeated Location location = 1; + message Location { + repeated int32 path = 1 [packed = true]; + repeated int32 span = 2 [packed = true]; + optional string leading_comments = 3; + optional string trailing_comments = 4; + repeated string leading_detached_comments = 6; + } + extensions 536000000 [ + declaration = { + number: 536000000, + full_name: ".buf.descriptor.v1.buf_source_code_info_extension", + type: ".buf.descriptor.v1.SourceCodeInfoExtension" + } + ]; +} +message UninterpretedOption { + repeated NamePart name = 2; + optional string identifier_value = 3; + optional uint64 positive_int_value = 4; + optional int64 negative_int_value = 5; + optional double double_value = 6; + optional bytes string_value = 7; + optional string aggregate_value = 8; + message NamePart { + required string name_part = 1; + required bool is_extension = 2; + } +} +enum Edition { + EDITION_UNKNOWN = 0; + EDITION_1_TEST_ONLY = 1; + EDITION_2_TEST_ONLY = 2; + EDITION_LEGACY = 900; + EDITION_PROTO2 = 998; + EDITION_PROTO3 = 999; + EDITION_2023 = 1000; + EDITION_2024 = 1001; + EDITION_99997_TEST_ONLY = 99997; + EDITION_99998_TEST_ONLY = 99998; + EDITION_99999_TEST_ONLY = 99999; + EDITION_MAX = 2147483647; +} +-- options.proto -- +syntax = "proto3"; +import "google/protobuf/descriptor.proto"; +message Files { + google.protobuf.FileDescriptorSet files = 1; +} +message UnusedOption { + string foo = 1; +} +message UsedOption { + string foo = 1; + extend google.protobuf.FileOptions { + UsedOption file_foo = 50000; + UnusedOption file_bar = 50001; + string file_baz = 50002; + } +} +extend google.protobuf.EnumOptions { + UsedOption enum_foo = 50000; + UnusedOption enum_bar = 50001; + string enum_baz = 50002; +} +extend google.protobuf.EnumValueOptions { + UsedOption enum_value_foo = 50000; + UnusedOption enum_value_bar = 50001; + string enum_value_baz = 50002; +} +extend google.protobuf.FieldOptions { + UsedOption field_foo = 50000; + UnusedOption field_bar = 50001; + string field_baz = 50002; +} +extend google.protobuf.MessageOptions { + UsedOption message_foo = 50000; + UnusedOption message_bar = 50001; + string message_baz = 50002; +} +extend google.protobuf.MethodOptions { + UsedOption method_foo = 50000; + UnusedOption method_bar = 50001; + string method_baz = 50002; +} +extend google.protobuf.OneofOptions { + UsedOption oneof_foo = 50000; + UnusedOption oneof_bar = 50001; + string oneof_baz = 50002; +} +extend google.protobuf.ServiceOptions { + UsedOption service_foo = 50000; + UnusedOption service_bar = 50001; + string service_baz = 50002; +} diff --git a/private/bufpkg/bufimage/bufimageutil/testdata/options/pkg.FooService.Do.exclude.txtar b/private/bufpkg/bufimage/bufimageutil/testdata/options/pkg.FooService.Do.exclude.txtar new file mode 100644 index 0000000000..31ef6f2cd1 --- /dev/null +++ b/private/bufpkg/bufimage/bufimageutil/testdata/options/pkg.FooService.Do.exclude.txtar @@ -0,0 +1,558 @@ +-- a.proto -- +syntax = "proto2"; +package pkg; +import "options.proto"; +option (UsedOption.file_baz) = "str"; +option (UsedOption.file_foo) = { foo: "str" }; +message Empty { +} +message Foo { + option (message_baz) = "str"; + option (message_foo) = { foo: "str" }; + optional uint64 foo = 1 [ + jstype = JS_STRING, + (field_baz) = "str", + (field_foo) = { foo: "str" } + ]; + oneof testOneof { + option (oneof_baz) = "str"; + option (oneof_foo) = { foo: "str" }; + string bar = 2; + bytes baz = 3; + } + extensions 10 to max; +} +enum FooEnum { + option deprecated = true; + option (enum_baz) = "str"; + option (enum_foo) = { foo: "str" }; + FOO_ENUM_X = 0; + FOO_ENUM_Y = 1 [ + (enum_value_baz) = "str", + (enum_value_foo) = { foo: "str" } + ]; +} +service FooService { + option (service_baz) = "str"; + option (service_foo) = { foo: "str" }; + rpc DoNot ( Empty ) returns ( Empty ) { + option (method_baz) = "str"; + option (method_foo) = { foo: "str" }; + } +} +extend Foo { + optional string extension = 11 [(field_baz) = "str", (field_foo) = { foo: "str" }]; +} +-- google/protobuf/descriptor.proto -- +syntax = "proto2"; +package google.protobuf; +option cc_enable_arenas = true; +option csharp_namespace = "Google.Protobuf.Reflection"; +option go_package = "google.golang.org/protobuf/types/descriptorpb"; +option java_outer_classname = "DescriptorProtos"; +option java_package = "com.google.protobuf"; +option objc_class_prefix = "GPB"; +option optimize_for = SPEED; +message DescriptorProto { + optional string name = 1; + repeated FieldDescriptorProto field = 2; + repeated DescriptorProto nested_type = 3; + repeated EnumDescriptorProto enum_type = 4; + repeated ExtensionRange extension_range = 5; + repeated FieldDescriptorProto extension = 6; + optional MessageOptions options = 7; + repeated OneofDescriptorProto oneof_decl = 8; + repeated ReservedRange reserved_range = 9; + repeated string reserved_name = 10; + message ExtensionRange { + optional int32 start = 1; + optional int32 end = 2; + optional ExtensionRangeOptions options = 3; + } + message ReservedRange { + optional int32 start = 1; + optional int32 end = 2; + } +} +message EnumDescriptorProto { + optional string name = 1; + repeated EnumValueDescriptorProto value = 2; + optional EnumOptions options = 3; + repeated EnumReservedRange reserved_range = 4; + repeated string reserved_name = 5; + message EnumReservedRange { + optional int32 start = 1; + optional int32 end = 2; + } +} +message EnumOptions { + optional bool allow_alias = 2; + optional bool deprecated = 3 [default = false]; + optional bool deprecated_legacy_json_field_conflicts = 6 [deprecated = true]; + optional FeatureSet features = 7; + repeated UninterpretedOption uninterpreted_option = 999; + extensions 1000 to max; + reserved 5; +} +message EnumValueDescriptorProto { + optional string name = 1; + optional int32 number = 2; + optional EnumValueOptions options = 3; +} +message EnumValueOptions { + optional bool deprecated = 1 [default = false]; + optional FeatureSet features = 2; + optional bool debug_redact = 3 [default = false]; + optional FieldOptions.FeatureSupport feature_support = 4; + repeated UninterpretedOption uninterpreted_option = 999; + extensions 1000 to max; +} +message ExtensionRangeOptions { + repeated Declaration declaration = 2 [retention = RETENTION_SOURCE]; + optional VerificationState verification = 3 [default = UNVERIFIED, retention = RETENTION_SOURCE]; + optional FeatureSet features = 50; + repeated UninterpretedOption uninterpreted_option = 999; + message Declaration { + optional int32 number = 1; + optional string full_name = 2; + optional string type = 3; + optional bool reserved = 5; + optional bool repeated = 6; + reserved 4; + } + enum VerificationState { + DECLARATION = 0; + UNVERIFIED = 1; + } + extensions 1000 to max; +} +message FeatureSet { + optional FieldPresence field_presence = 1 [ + edition_defaults = { value: "EXPLICIT", edition: EDITION_LEGACY }, + edition_defaults = { value: "IMPLICIT", edition: EDITION_PROTO3 }, + edition_defaults = { value: "EXPLICIT", edition: EDITION_2023 }, + feature_support = { edition_introduced: EDITION_2023 }, + retention = RETENTION_RUNTIME, + targets = TARGET_TYPE_FIELD, + targets = TARGET_TYPE_FILE + ]; + optional EnumType enum_type = 2 [ + edition_defaults = { value: "CLOSED", edition: EDITION_LEGACY }, + edition_defaults = { value: "OPEN", edition: EDITION_PROTO3 }, + feature_support = { edition_introduced: EDITION_2023 }, + retention = RETENTION_RUNTIME, + targets = TARGET_TYPE_ENUM, + targets = TARGET_TYPE_FILE + ]; + optional RepeatedFieldEncoding repeated_field_encoding = 3 [ + edition_defaults = { value: "EXPANDED", edition: EDITION_LEGACY }, + edition_defaults = { value: "PACKED", edition: EDITION_PROTO3 }, + feature_support = { edition_introduced: EDITION_2023 }, + retention = RETENTION_RUNTIME, + targets = TARGET_TYPE_FIELD, + targets = TARGET_TYPE_FILE + ]; + optional Utf8Validation utf8_validation = 4 [ + edition_defaults = { value: "NONE", edition: EDITION_LEGACY }, + edition_defaults = { value: "VERIFY", edition: EDITION_PROTO3 }, + feature_support = { edition_introduced: EDITION_2023 }, + retention = RETENTION_RUNTIME, + targets = TARGET_TYPE_FIELD, + targets = TARGET_TYPE_FILE + ]; + optional MessageEncoding message_encoding = 5 [ + edition_defaults = { value: "LENGTH_PREFIXED", edition: EDITION_LEGACY }, + feature_support = { edition_introduced: EDITION_2023 }, + retention = RETENTION_RUNTIME, + targets = TARGET_TYPE_FIELD, + targets = TARGET_TYPE_FILE + ]; + optional JsonFormat json_format = 6 [ + edition_defaults = { value: "LEGACY_BEST_EFFORT", edition: EDITION_LEGACY }, + edition_defaults = { value: "ALLOW", edition: EDITION_PROTO3 }, + feature_support = { edition_introduced: EDITION_2023 }, + retention = RETENTION_RUNTIME, + targets = TARGET_TYPE_MESSAGE, + targets = TARGET_TYPE_ENUM, + targets = TARGET_TYPE_FILE + ]; + enum EnumType { + ENUM_TYPE_UNKNOWN = 0; + OPEN = 1; + CLOSED = 2; + } + enum FieldPresence { + FIELD_PRESENCE_UNKNOWN = 0; + EXPLICIT = 1; + IMPLICIT = 2; + LEGACY_REQUIRED = 3; + } + enum JsonFormat { + JSON_FORMAT_UNKNOWN = 0; + ALLOW = 1; + LEGACY_BEST_EFFORT = 2; + } + enum MessageEncoding { + MESSAGE_ENCODING_UNKNOWN = 0; + LENGTH_PREFIXED = 1; + DELIMITED = 2; + } + enum RepeatedFieldEncoding { + REPEATED_FIELD_ENCODING_UNKNOWN = 0; + PACKED = 1; + EXPANDED = 2; + } + enum Utf8Validation { + UTF8_VALIDATION_UNKNOWN = 0; + VERIFY = 2; + NONE = 3; + reserved 1; + } + extensions 1000 to 9994 [ + declaration = { + number: 1000, + full_name: ".pb.cpp", + type: ".pb.CppFeatures" + }, + declaration = { + number: 1001, + full_name: ".pb.java", + type: ".pb.JavaFeatures" + }, + declaration = { + number: 1002, + full_name: ".pb.go", + type: ".pb.GoFeatures" + }, + declaration = { + number: 9990, + full_name: ".pb.proto1", + type: ".pb.Proto1Features" + } + ]; + extensions 9995 to 9999, 10000; + reserved 999; +} +message FeatureSetDefaults { + repeated FeatureSetEditionDefault defaults = 1; + optional Edition minimum_edition = 4; + optional Edition maximum_edition = 5; + message FeatureSetEditionDefault { + optional Edition edition = 3; + optional FeatureSet overridable_features = 4; + optional FeatureSet fixed_features = 5; + reserved 1, 2; + reserved "features"; + } +} +message FieldDescriptorProto { + optional string name = 1; + optional string extendee = 2; + optional int32 number = 3; + optional Label label = 4; + optional Type type = 5; + optional string type_name = 6; + optional string default_value = 7; + optional FieldOptions options = 8; + optional int32 oneof_index = 9; + optional string json_name = 10; + optional bool proto3_optional = 17; + enum Label { + LABEL_OPTIONAL = 1; + LABEL_REQUIRED = 2; + LABEL_REPEATED = 3; + } + enum Type { + TYPE_DOUBLE = 1; + TYPE_FLOAT = 2; + TYPE_INT64 = 3; + TYPE_UINT64 = 4; + TYPE_INT32 = 5; + TYPE_FIXED64 = 6; + TYPE_FIXED32 = 7; + TYPE_BOOL = 8; + TYPE_STRING = 9; + TYPE_GROUP = 10; + TYPE_MESSAGE = 11; + TYPE_BYTES = 12; + TYPE_UINT32 = 13; + TYPE_ENUM = 14; + TYPE_SFIXED32 = 15; + TYPE_SFIXED64 = 16; + TYPE_SINT32 = 17; + TYPE_SINT64 = 18; + } +} +message FieldOptions { + optional CType ctype = 1 [default = STRING]; + optional bool packed = 2; + optional bool deprecated = 3 [default = false]; + optional bool lazy = 5 [default = false]; + optional JSType jstype = 6 [default = JS_NORMAL]; + optional bool weak = 10 [default = false]; + optional bool unverified_lazy = 15 [default = false]; + optional bool debug_redact = 16 [default = false]; + optional OptionRetention retention = 17; + repeated OptionTargetType targets = 19; + repeated EditionDefault edition_defaults = 20; + optional FeatureSet features = 21; + optional FeatureSupport feature_support = 22; + repeated UninterpretedOption uninterpreted_option = 999; + message EditionDefault { + optional string value = 2; + optional Edition edition = 3; + } + message FeatureSupport { + optional Edition edition_introduced = 1; + optional Edition edition_deprecated = 2; + optional string deprecation_warning = 3; + optional Edition edition_removed = 4; + } + enum CType { + STRING = 0; + CORD = 1; + STRING_PIECE = 2; + } + enum JSType { + JS_NORMAL = 0; + JS_STRING = 1; + JS_NUMBER = 2; + } + enum OptionRetention { + RETENTION_UNKNOWN = 0; + RETENTION_RUNTIME = 1; + RETENTION_SOURCE = 2; + } + enum OptionTargetType { + TARGET_TYPE_UNKNOWN = 0; + TARGET_TYPE_FILE = 1; + TARGET_TYPE_EXTENSION_RANGE = 2; + TARGET_TYPE_MESSAGE = 3; + TARGET_TYPE_FIELD = 4; + TARGET_TYPE_ONEOF = 5; + TARGET_TYPE_ENUM = 6; + TARGET_TYPE_ENUM_ENTRY = 7; + TARGET_TYPE_SERVICE = 8; + TARGET_TYPE_METHOD = 9; + } + extensions 1000 to max; + reserved 4, 18; +} +message FileDescriptorProto { + optional string name = 1; + optional string package = 2; + repeated string dependency = 3; + repeated DescriptorProto message_type = 4; + repeated EnumDescriptorProto enum_type = 5; + repeated ServiceDescriptorProto service = 6; + repeated FieldDescriptorProto extension = 7; + optional FileOptions options = 8; + optional SourceCodeInfo source_code_info = 9; + repeated int32 public_dependency = 10; + repeated int32 weak_dependency = 11; + optional string syntax = 12; + optional Edition edition = 14; +} +message FileDescriptorSet { + repeated FileDescriptorProto file = 1; + extensions 536000000 [ + declaration = { + number: 536000000, + full_name: ".buf.descriptor.v1.buf_file_descriptor_set_extension", + type: ".buf.descriptor.v1.FileDescriptorSetExtension" + } + ]; +} +message FileOptions { + optional string java_package = 1; + optional string java_outer_classname = 8; + optional OptimizeMode optimize_for = 9 [default = SPEED]; + optional bool java_multiple_files = 10 [default = false]; + optional string go_package = 11; + optional bool cc_generic_services = 16 [default = false]; + optional bool java_generic_services = 17 [default = false]; + optional bool py_generic_services = 18 [default = false]; + optional bool java_generate_equals_and_hash = 20 [deprecated = true]; + optional bool deprecated = 23 [default = false]; + optional bool java_string_check_utf8 = 27 [default = false]; + optional bool cc_enable_arenas = 31 [default = true]; + optional string objc_class_prefix = 36; + optional string csharp_namespace = 37; + optional string swift_prefix = 39; + optional string php_class_prefix = 40; + optional string php_namespace = 41; + optional string php_metadata_namespace = 44; + optional string ruby_package = 45; + optional FeatureSet features = 50; + repeated UninterpretedOption uninterpreted_option = 999; + enum OptimizeMode { + SPEED = 1; + CODE_SIZE = 2; + LITE_RUNTIME = 3; + } + extensions 1000 to max; + reserved 38, 42; + reserved "php_generic_services"; +} +message GeneratedCodeInfo { + repeated Annotation annotation = 1; + message Annotation { + repeated int32 path = 1 [packed = true]; + optional string source_file = 2; + optional int32 begin = 3; + optional int32 end = 4; + optional Semantic semantic = 5; + enum Semantic { + NONE = 0; + SET = 1; + ALIAS = 2; + } + } +} +message MessageOptions { + optional bool message_set_wire_format = 1 [default = false]; + optional bool no_standard_descriptor_accessor = 2 [default = false]; + optional bool deprecated = 3 [default = false]; + optional bool map_entry = 7; + optional bool deprecated_legacy_json_field_conflicts = 11 [deprecated = true]; + optional FeatureSet features = 12; + repeated UninterpretedOption uninterpreted_option = 999; + extensions 1000 to max; + reserved 4, 5, 6, 8, 9; +} +message MethodDescriptorProto { + optional string name = 1; + optional string input_type = 2; + optional string output_type = 3; + optional MethodOptions options = 4; + optional bool client_streaming = 5 [default = false]; + optional bool server_streaming = 6 [default = false]; +} +message MethodOptions { + optional bool deprecated = 33 [default = false]; + optional IdempotencyLevel idempotency_level = 34 [default = IDEMPOTENCY_UNKNOWN]; + optional FeatureSet features = 35; + repeated UninterpretedOption uninterpreted_option = 999; + enum IdempotencyLevel { + IDEMPOTENCY_UNKNOWN = 0; + NO_SIDE_EFFECTS = 1; + IDEMPOTENT = 2; + } + extensions 1000 to max; +} +message OneofDescriptorProto { + optional string name = 1; + optional OneofOptions options = 2; +} +message OneofOptions { + optional FeatureSet features = 1; + repeated UninterpretedOption uninterpreted_option = 999; + extensions 1000 to max; +} +message ServiceDescriptorProto { + optional string name = 1; + repeated MethodDescriptorProto method = 2; + optional ServiceOptions options = 3; +} +message ServiceOptions { + optional bool deprecated = 33 [default = false]; + optional FeatureSet features = 34; + repeated UninterpretedOption uninterpreted_option = 999; + extensions 1000 to max; +} +message SourceCodeInfo { + repeated Location location = 1; + message Location { + repeated int32 path = 1 [packed = true]; + repeated int32 span = 2 [packed = true]; + optional string leading_comments = 3; + optional string trailing_comments = 4; + repeated string leading_detached_comments = 6; + } + extensions 536000000 [ + declaration = { + number: 536000000, + full_name: ".buf.descriptor.v1.buf_source_code_info_extension", + type: ".buf.descriptor.v1.SourceCodeInfoExtension" + } + ]; +} +message UninterpretedOption { + repeated NamePart name = 2; + optional string identifier_value = 3; + optional uint64 positive_int_value = 4; + optional int64 negative_int_value = 5; + optional double double_value = 6; + optional bytes string_value = 7; + optional string aggregate_value = 8; + message NamePart { + required string name_part = 1; + required bool is_extension = 2; + } +} +enum Edition { + EDITION_UNKNOWN = 0; + EDITION_1_TEST_ONLY = 1; + EDITION_2_TEST_ONLY = 2; + EDITION_LEGACY = 900; + EDITION_PROTO2 = 998; + EDITION_PROTO3 = 999; + EDITION_2023 = 1000; + EDITION_2024 = 1001; + EDITION_99997_TEST_ONLY = 99997; + EDITION_99998_TEST_ONLY = 99998; + EDITION_99999_TEST_ONLY = 99999; + EDITION_MAX = 2147483647; +} +-- options.proto -- +syntax = "proto3"; +import "google/protobuf/descriptor.proto"; +message Files { + google.protobuf.FileDescriptorSet files = 1; +} +message UnusedOption { + string foo = 1; +} +message UsedOption { + string foo = 1; + extend google.protobuf.FileOptions { + UsedOption file_foo = 50000; + UnusedOption file_bar = 50001; + string file_baz = 50002; + } +} +extend google.protobuf.EnumOptions { + UsedOption enum_foo = 50000; + UnusedOption enum_bar = 50001; + string enum_baz = 50002; +} +extend google.protobuf.EnumValueOptions { + UsedOption enum_value_foo = 50000; + UnusedOption enum_value_bar = 50001; + string enum_value_baz = 50002; +} +extend google.protobuf.FieldOptions { + UsedOption field_foo = 50000; + UnusedOption field_bar = 50001; + string field_baz = 50002; +} +extend google.protobuf.MessageOptions { + UsedOption message_foo = 50000; + UnusedOption message_bar = 50001; + string message_baz = 50002; +} +extend google.protobuf.MethodOptions { + UsedOption method_foo = 50000; + UnusedOption method_bar = 50001; + string method_baz = 50002; +} +extend google.protobuf.OneofOptions { + UsedOption oneof_foo = 50000; + UnusedOption oneof_bar = 50001; + string oneof_baz = 50002; +} +extend google.protobuf.ServiceOptions { + UsedOption service_foo = 50000; + UnusedOption service_bar = 50001; + string service_baz = 50002; +} diff --git a/private/bufpkg/bufimage/bufimageutil/testdata/options/pkg.FooService.exclude-method-types.txtar b/private/bufpkg/bufimage/bufimageutil/testdata/options/pkg.FooService.exclude-method-types.txtar new file mode 100644 index 0000000000..d217c0ee80 --- /dev/null +++ b/private/bufpkg/bufimage/bufimageutil/testdata/options/pkg.FooService.exclude-method-types.txtar @@ -0,0 +1,191 @@ +-- a.proto -- +syntax = "proto2"; +package pkg; +import "options.proto"; +option (UsedOption.file_baz) = "str"; +option (UsedOption.file_foo) = { foo: "str" }; +service FooService { + option (service_baz) = "str"; + option (service_foo) = { foo: "str" }; +} +-- google/protobuf/descriptor.proto -- +syntax = "proto2"; +package google.protobuf; +option cc_enable_arenas = true; +option csharp_namespace = "Google.Protobuf.Reflection"; +option go_package = "google.golang.org/protobuf/types/descriptorpb"; +option java_outer_classname = "DescriptorProtos"; +option java_package = "com.google.protobuf"; +option objc_class_prefix = "GPB"; +option optimize_for = SPEED; +message FeatureSet { + optional FieldPresence field_presence = 1 [ + edition_defaults = { value: "EXPLICIT", edition: EDITION_LEGACY }, + edition_defaults = { value: "IMPLICIT", edition: EDITION_PROTO3 }, + edition_defaults = { value: "EXPLICIT", edition: EDITION_2023 }, + feature_support = { edition_introduced: EDITION_2023 }, + retention = RETENTION_RUNTIME, + targets = TARGET_TYPE_FIELD, + targets = TARGET_TYPE_FILE + ]; + optional EnumType enum_type = 2 [ + edition_defaults = { value: "CLOSED", edition: EDITION_LEGACY }, + edition_defaults = { value: "OPEN", edition: EDITION_PROTO3 }, + feature_support = { edition_introduced: EDITION_2023 }, + retention = RETENTION_RUNTIME, + targets = TARGET_TYPE_ENUM, + targets = TARGET_TYPE_FILE + ]; + optional RepeatedFieldEncoding repeated_field_encoding = 3 [ + edition_defaults = { value: "EXPANDED", edition: EDITION_LEGACY }, + edition_defaults = { value: "PACKED", edition: EDITION_PROTO3 }, + feature_support = { edition_introduced: EDITION_2023 }, + retention = RETENTION_RUNTIME, + targets = TARGET_TYPE_FIELD, + targets = TARGET_TYPE_FILE + ]; + optional Utf8Validation utf8_validation = 4 [ + edition_defaults = { value: "NONE", edition: EDITION_LEGACY }, + edition_defaults = { value: "VERIFY", edition: EDITION_PROTO3 }, + feature_support = { edition_introduced: EDITION_2023 }, + retention = RETENTION_RUNTIME, + targets = TARGET_TYPE_FIELD, + targets = TARGET_TYPE_FILE + ]; + optional MessageEncoding message_encoding = 5 [ + edition_defaults = { value: "LENGTH_PREFIXED", edition: EDITION_LEGACY }, + feature_support = { edition_introduced: EDITION_2023 }, + retention = RETENTION_RUNTIME, + targets = TARGET_TYPE_FIELD, + targets = TARGET_TYPE_FILE + ]; + optional JsonFormat json_format = 6 [ + edition_defaults = { value: "LEGACY_BEST_EFFORT", edition: EDITION_LEGACY }, + edition_defaults = { value: "ALLOW", edition: EDITION_PROTO3 }, + feature_support = { edition_introduced: EDITION_2023 }, + retention = RETENTION_RUNTIME, + targets = TARGET_TYPE_MESSAGE, + targets = TARGET_TYPE_ENUM, + targets = TARGET_TYPE_FILE + ]; + enum EnumType { + ENUM_TYPE_UNKNOWN = 0; + OPEN = 1; + CLOSED = 2; + } + enum FieldPresence { + FIELD_PRESENCE_UNKNOWN = 0; + EXPLICIT = 1; + IMPLICIT = 2; + LEGACY_REQUIRED = 3; + } + enum JsonFormat { + JSON_FORMAT_UNKNOWN = 0; + ALLOW = 1; + LEGACY_BEST_EFFORT = 2; + } + enum MessageEncoding { + MESSAGE_ENCODING_UNKNOWN = 0; + LENGTH_PREFIXED = 1; + DELIMITED = 2; + } + enum RepeatedFieldEncoding { + REPEATED_FIELD_ENCODING_UNKNOWN = 0; + PACKED = 1; + EXPANDED = 2; + } + enum Utf8Validation { + UTF8_VALIDATION_UNKNOWN = 0; + VERIFY = 2; + NONE = 3; + reserved 1; + } + extensions 1000 to 9994 [ + declaration = { + number: 1000, + full_name: ".pb.cpp", + type: ".pb.CppFeatures" + }, + declaration = { + number: 1001, + full_name: ".pb.java", + type: ".pb.JavaFeatures" + }, + declaration = { + number: 1002, + full_name: ".pb.go", + type: ".pb.GoFeatures" + }, + declaration = { + number: 9990, + full_name: ".pb.proto1", + type: ".pb.Proto1Features" + } + ]; + extensions 9995 to 9999, 10000; + reserved 999; +} +message FileOptions { + optional string java_package = 1; + optional string java_outer_classname = 8; + optional OptimizeMode optimize_for = 9 [default = SPEED]; + optional bool java_multiple_files = 10 [default = false]; + optional string go_package = 11; + optional bool cc_generic_services = 16 [default = false]; + optional bool java_generic_services = 17 [default = false]; + optional bool py_generic_services = 18 [default = false]; + optional bool java_generate_equals_and_hash = 20 [deprecated = true]; + optional bool deprecated = 23 [default = false]; + optional bool java_string_check_utf8 = 27 [default = false]; + optional bool cc_enable_arenas = 31 [default = true]; + optional string objc_class_prefix = 36; + optional string csharp_namespace = 37; + optional string swift_prefix = 39; + optional string php_class_prefix = 40; + optional string php_namespace = 41; + optional string php_metadata_namespace = 44; + optional string ruby_package = 45; + optional FeatureSet features = 50; + repeated UninterpretedOption uninterpreted_option = 999; + enum OptimizeMode { + SPEED = 1; + CODE_SIZE = 2; + LITE_RUNTIME = 3; + } + extensions 1000 to max; + reserved 38, 42; + reserved "php_generic_services"; +} +message ServiceOptions { + optional bool deprecated = 33 [default = false]; + optional FeatureSet features = 34; + repeated UninterpretedOption uninterpreted_option = 999; + extensions 1000 to max; +} +message UninterpretedOption { + repeated NamePart name = 2; + optional string identifier_value = 3; + optional uint64 positive_int_value = 4; + optional int64 negative_int_value = 5; + optional double double_value = 6; + optional bytes string_value = 7; + optional string aggregate_value = 8; + message NamePart { + required string name_part = 1; + required bool is_extension = 2; + } +} +-- options.proto -- +syntax = "proto3"; +import "google/protobuf/descriptor.proto"; +message UsedOption { + string foo = 1; + extend google.protobuf.FileOptions { + UsedOption file_foo = 50000; + string file_baz = 50002; + } +} +extend google.protobuf.ServiceOptions { + UsedOption service_foo = 50000; + string service_baz = 50002; +} diff --git a/private/bufpkg/bufimage/bufimageutil/testdata/options/pkg.FooService.exclude.txtar b/private/bufpkg/bufimage/bufimageutil/testdata/options/pkg.FooService.exclude.txtar new file mode 100644 index 0000000000..6d617bde4b --- /dev/null +++ b/private/bufpkg/bufimage/bufimageutil/testdata/options/pkg.FooService.exclude.txtar @@ -0,0 +1,550 @@ +-- a.proto -- +syntax = "proto2"; +package pkg; +import "options.proto"; +option (UsedOption.file_baz) = "str"; +option (UsedOption.file_foo) = { foo: "str" }; +message Empty { +} +message Foo { + option (message_baz) = "str"; + option (message_foo) = { foo: "str" }; + optional uint64 foo = 1 [ + jstype = JS_STRING, + (field_baz) = "str", + (field_foo) = { foo: "str" } + ]; + oneof testOneof { + option (oneof_baz) = "str"; + option (oneof_foo) = { foo: "str" }; + string bar = 2; + bytes baz = 3; + } + extensions 10 to max; +} +enum FooEnum { + option deprecated = true; + option (enum_baz) = "str"; + option (enum_foo) = { foo: "str" }; + FOO_ENUM_X = 0; + FOO_ENUM_Y = 1 [ + (enum_value_baz) = "str", + (enum_value_foo) = { foo: "str" } + ]; +} +extend Foo { + optional string extension = 11 [(field_baz) = "str", (field_foo) = { foo: "str" }]; +} +-- google/protobuf/descriptor.proto -- +syntax = "proto2"; +package google.protobuf; +option cc_enable_arenas = true; +option csharp_namespace = "Google.Protobuf.Reflection"; +option go_package = "google.golang.org/protobuf/types/descriptorpb"; +option java_outer_classname = "DescriptorProtos"; +option java_package = "com.google.protobuf"; +option objc_class_prefix = "GPB"; +option optimize_for = SPEED; +message DescriptorProto { + optional string name = 1; + repeated FieldDescriptorProto field = 2; + repeated DescriptorProto nested_type = 3; + repeated EnumDescriptorProto enum_type = 4; + repeated ExtensionRange extension_range = 5; + repeated FieldDescriptorProto extension = 6; + optional MessageOptions options = 7; + repeated OneofDescriptorProto oneof_decl = 8; + repeated ReservedRange reserved_range = 9; + repeated string reserved_name = 10; + message ExtensionRange { + optional int32 start = 1; + optional int32 end = 2; + optional ExtensionRangeOptions options = 3; + } + message ReservedRange { + optional int32 start = 1; + optional int32 end = 2; + } +} +message EnumDescriptorProto { + optional string name = 1; + repeated EnumValueDescriptorProto value = 2; + optional EnumOptions options = 3; + repeated EnumReservedRange reserved_range = 4; + repeated string reserved_name = 5; + message EnumReservedRange { + optional int32 start = 1; + optional int32 end = 2; + } +} +message EnumOptions { + optional bool allow_alias = 2; + optional bool deprecated = 3 [default = false]; + optional bool deprecated_legacy_json_field_conflicts = 6 [deprecated = true]; + optional FeatureSet features = 7; + repeated UninterpretedOption uninterpreted_option = 999; + extensions 1000 to max; + reserved 5; +} +message EnumValueDescriptorProto { + optional string name = 1; + optional int32 number = 2; + optional EnumValueOptions options = 3; +} +message EnumValueOptions { + optional bool deprecated = 1 [default = false]; + optional FeatureSet features = 2; + optional bool debug_redact = 3 [default = false]; + optional FieldOptions.FeatureSupport feature_support = 4; + repeated UninterpretedOption uninterpreted_option = 999; + extensions 1000 to max; +} +message ExtensionRangeOptions { + repeated Declaration declaration = 2 [retention = RETENTION_SOURCE]; + optional VerificationState verification = 3 [default = UNVERIFIED, retention = RETENTION_SOURCE]; + optional FeatureSet features = 50; + repeated UninterpretedOption uninterpreted_option = 999; + message Declaration { + optional int32 number = 1; + optional string full_name = 2; + optional string type = 3; + optional bool reserved = 5; + optional bool repeated = 6; + reserved 4; + } + enum VerificationState { + DECLARATION = 0; + UNVERIFIED = 1; + } + extensions 1000 to max; +} +message FeatureSet { + optional FieldPresence field_presence = 1 [ + edition_defaults = { value: "EXPLICIT", edition: EDITION_LEGACY }, + edition_defaults = { value: "IMPLICIT", edition: EDITION_PROTO3 }, + edition_defaults = { value: "EXPLICIT", edition: EDITION_2023 }, + feature_support = { edition_introduced: EDITION_2023 }, + retention = RETENTION_RUNTIME, + targets = TARGET_TYPE_FIELD, + targets = TARGET_TYPE_FILE + ]; + optional EnumType enum_type = 2 [ + edition_defaults = { value: "CLOSED", edition: EDITION_LEGACY }, + edition_defaults = { value: "OPEN", edition: EDITION_PROTO3 }, + feature_support = { edition_introduced: EDITION_2023 }, + retention = RETENTION_RUNTIME, + targets = TARGET_TYPE_ENUM, + targets = TARGET_TYPE_FILE + ]; + optional RepeatedFieldEncoding repeated_field_encoding = 3 [ + edition_defaults = { value: "EXPANDED", edition: EDITION_LEGACY }, + edition_defaults = { value: "PACKED", edition: EDITION_PROTO3 }, + feature_support = { edition_introduced: EDITION_2023 }, + retention = RETENTION_RUNTIME, + targets = TARGET_TYPE_FIELD, + targets = TARGET_TYPE_FILE + ]; + optional Utf8Validation utf8_validation = 4 [ + edition_defaults = { value: "NONE", edition: EDITION_LEGACY }, + edition_defaults = { value: "VERIFY", edition: EDITION_PROTO3 }, + feature_support = { edition_introduced: EDITION_2023 }, + retention = RETENTION_RUNTIME, + targets = TARGET_TYPE_FIELD, + targets = TARGET_TYPE_FILE + ]; + optional MessageEncoding message_encoding = 5 [ + edition_defaults = { value: "LENGTH_PREFIXED", edition: EDITION_LEGACY }, + feature_support = { edition_introduced: EDITION_2023 }, + retention = RETENTION_RUNTIME, + targets = TARGET_TYPE_FIELD, + targets = TARGET_TYPE_FILE + ]; + optional JsonFormat json_format = 6 [ + edition_defaults = { value: "LEGACY_BEST_EFFORT", edition: EDITION_LEGACY }, + edition_defaults = { value: "ALLOW", edition: EDITION_PROTO3 }, + feature_support = { edition_introduced: EDITION_2023 }, + retention = RETENTION_RUNTIME, + targets = TARGET_TYPE_MESSAGE, + targets = TARGET_TYPE_ENUM, + targets = TARGET_TYPE_FILE + ]; + enum EnumType { + ENUM_TYPE_UNKNOWN = 0; + OPEN = 1; + CLOSED = 2; + } + enum FieldPresence { + FIELD_PRESENCE_UNKNOWN = 0; + EXPLICIT = 1; + IMPLICIT = 2; + LEGACY_REQUIRED = 3; + } + enum JsonFormat { + JSON_FORMAT_UNKNOWN = 0; + ALLOW = 1; + LEGACY_BEST_EFFORT = 2; + } + enum MessageEncoding { + MESSAGE_ENCODING_UNKNOWN = 0; + LENGTH_PREFIXED = 1; + DELIMITED = 2; + } + enum RepeatedFieldEncoding { + REPEATED_FIELD_ENCODING_UNKNOWN = 0; + PACKED = 1; + EXPANDED = 2; + } + enum Utf8Validation { + UTF8_VALIDATION_UNKNOWN = 0; + VERIFY = 2; + NONE = 3; + reserved 1; + } + extensions 1000 to 9994 [ + declaration = { + number: 1000, + full_name: ".pb.cpp", + type: ".pb.CppFeatures" + }, + declaration = { + number: 1001, + full_name: ".pb.java", + type: ".pb.JavaFeatures" + }, + declaration = { + number: 1002, + full_name: ".pb.go", + type: ".pb.GoFeatures" + }, + declaration = { + number: 9990, + full_name: ".pb.proto1", + type: ".pb.Proto1Features" + } + ]; + extensions 9995 to 9999, 10000; + reserved 999; +} +message FeatureSetDefaults { + repeated FeatureSetEditionDefault defaults = 1; + optional Edition minimum_edition = 4; + optional Edition maximum_edition = 5; + message FeatureSetEditionDefault { + optional Edition edition = 3; + optional FeatureSet overridable_features = 4; + optional FeatureSet fixed_features = 5; + reserved 1, 2; + reserved "features"; + } +} +message FieldDescriptorProto { + optional string name = 1; + optional string extendee = 2; + optional int32 number = 3; + optional Label label = 4; + optional Type type = 5; + optional string type_name = 6; + optional string default_value = 7; + optional FieldOptions options = 8; + optional int32 oneof_index = 9; + optional string json_name = 10; + optional bool proto3_optional = 17; + enum Label { + LABEL_OPTIONAL = 1; + LABEL_REQUIRED = 2; + LABEL_REPEATED = 3; + } + enum Type { + TYPE_DOUBLE = 1; + TYPE_FLOAT = 2; + TYPE_INT64 = 3; + TYPE_UINT64 = 4; + TYPE_INT32 = 5; + TYPE_FIXED64 = 6; + TYPE_FIXED32 = 7; + TYPE_BOOL = 8; + TYPE_STRING = 9; + TYPE_GROUP = 10; + TYPE_MESSAGE = 11; + TYPE_BYTES = 12; + TYPE_UINT32 = 13; + TYPE_ENUM = 14; + TYPE_SFIXED32 = 15; + TYPE_SFIXED64 = 16; + TYPE_SINT32 = 17; + TYPE_SINT64 = 18; + } +} +message FieldOptions { + optional CType ctype = 1 [default = STRING]; + optional bool packed = 2; + optional bool deprecated = 3 [default = false]; + optional bool lazy = 5 [default = false]; + optional JSType jstype = 6 [default = JS_NORMAL]; + optional bool weak = 10 [default = false]; + optional bool unverified_lazy = 15 [default = false]; + optional bool debug_redact = 16 [default = false]; + optional OptionRetention retention = 17; + repeated OptionTargetType targets = 19; + repeated EditionDefault edition_defaults = 20; + optional FeatureSet features = 21; + optional FeatureSupport feature_support = 22; + repeated UninterpretedOption uninterpreted_option = 999; + message EditionDefault { + optional string value = 2; + optional Edition edition = 3; + } + message FeatureSupport { + optional Edition edition_introduced = 1; + optional Edition edition_deprecated = 2; + optional string deprecation_warning = 3; + optional Edition edition_removed = 4; + } + enum CType { + STRING = 0; + CORD = 1; + STRING_PIECE = 2; + } + enum JSType { + JS_NORMAL = 0; + JS_STRING = 1; + JS_NUMBER = 2; + } + enum OptionRetention { + RETENTION_UNKNOWN = 0; + RETENTION_RUNTIME = 1; + RETENTION_SOURCE = 2; + } + enum OptionTargetType { + TARGET_TYPE_UNKNOWN = 0; + TARGET_TYPE_FILE = 1; + TARGET_TYPE_EXTENSION_RANGE = 2; + TARGET_TYPE_MESSAGE = 3; + TARGET_TYPE_FIELD = 4; + TARGET_TYPE_ONEOF = 5; + TARGET_TYPE_ENUM = 6; + TARGET_TYPE_ENUM_ENTRY = 7; + TARGET_TYPE_SERVICE = 8; + TARGET_TYPE_METHOD = 9; + } + extensions 1000 to max; + reserved 4, 18; +} +message FileDescriptorProto { + optional string name = 1; + optional string package = 2; + repeated string dependency = 3; + repeated DescriptorProto message_type = 4; + repeated EnumDescriptorProto enum_type = 5; + repeated ServiceDescriptorProto service = 6; + repeated FieldDescriptorProto extension = 7; + optional FileOptions options = 8; + optional SourceCodeInfo source_code_info = 9; + repeated int32 public_dependency = 10; + repeated int32 weak_dependency = 11; + optional string syntax = 12; + optional Edition edition = 14; +} +message FileDescriptorSet { + repeated FileDescriptorProto file = 1; + extensions 536000000 [ + declaration = { + number: 536000000, + full_name: ".buf.descriptor.v1.buf_file_descriptor_set_extension", + type: ".buf.descriptor.v1.FileDescriptorSetExtension" + } + ]; +} +message FileOptions { + optional string java_package = 1; + optional string java_outer_classname = 8; + optional OptimizeMode optimize_for = 9 [default = SPEED]; + optional bool java_multiple_files = 10 [default = false]; + optional string go_package = 11; + optional bool cc_generic_services = 16 [default = false]; + optional bool java_generic_services = 17 [default = false]; + optional bool py_generic_services = 18 [default = false]; + optional bool java_generate_equals_and_hash = 20 [deprecated = true]; + optional bool deprecated = 23 [default = false]; + optional bool java_string_check_utf8 = 27 [default = false]; + optional bool cc_enable_arenas = 31 [default = true]; + optional string objc_class_prefix = 36; + optional string csharp_namespace = 37; + optional string swift_prefix = 39; + optional string php_class_prefix = 40; + optional string php_namespace = 41; + optional string php_metadata_namespace = 44; + optional string ruby_package = 45; + optional FeatureSet features = 50; + repeated UninterpretedOption uninterpreted_option = 999; + enum OptimizeMode { + SPEED = 1; + CODE_SIZE = 2; + LITE_RUNTIME = 3; + } + extensions 1000 to max; + reserved 38, 42; + reserved "php_generic_services"; +} +message GeneratedCodeInfo { + repeated Annotation annotation = 1; + message Annotation { + repeated int32 path = 1 [packed = true]; + optional string source_file = 2; + optional int32 begin = 3; + optional int32 end = 4; + optional Semantic semantic = 5; + enum Semantic { + NONE = 0; + SET = 1; + ALIAS = 2; + } + } +} +message MessageOptions { + optional bool message_set_wire_format = 1 [default = false]; + optional bool no_standard_descriptor_accessor = 2 [default = false]; + optional bool deprecated = 3 [default = false]; + optional bool map_entry = 7; + optional bool deprecated_legacy_json_field_conflicts = 11 [deprecated = true]; + optional FeatureSet features = 12; + repeated UninterpretedOption uninterpreted_option = 999; + extensions 1000 to max; + reserved 4, 5, 6, 8, 9; +} +message MethodDescriptorProto { + optional string name = 1; + optional string input_type = 2; + optional string output_type = 3; + optional MethodOptions options = 4; + optional bool client_streaming = 5 [default = false]; + optional bool server_streaming = 6 [default = false]; +} +message MethodOptions { + optional bool deprecated = 33 [default = false]; + optional IdempotencyLevel idempotency_level = 34 [default = IDEMPOTENCY_UNKNOWN]; + optional FeatureSet features = 35; + repeated UninterpretedOption uninterpreted_option = 999; + enum IdempotencyLevel { + IDEMPOTENCY_UNKNOWN = 0; + NO_SIDE_EFFECTS = 1; + IDEMPOTENT = 2; + } + extensions 1000 to max; +} +message OneofDescriptorProto { + optional string name = 1; + optional OneofOptions options = 2; +} +message OneofOptions { + optional FeatureSet features = 1; + repeated UninterpretedOption uninterpreted_option = 999; + extensions 1000 to max; +} +message ServiceDescriptorProto { + optional string name = 1; + repeated MethodDescriptorProto method = 2; + optional ServiceOptions options = 3; +} +message ServiceOptions { + optional bool deprecated = 33 [default = false]; + optional FeatureSet features = 34; + repeated UninterpretedOption uninterpreted_option = 999; + extensions 1000 to max; +} +message SourceCodeInfo { + repeated Location location = 1; + message Location { + repeated int32 path = 1 [packed = true]; + repeated int32 span = 2 [packed = true]; + optional string leading_comments = 3; + optional string trailing_comments = 4; + repeated string leading_detached_comments = 6; + } + extensions 536000000 [ + declaration = { + number: 536000000, + full_name: ".buf.descriptor.v1.buf_source_code_info_extension", + type: ".buf.descriptor.v1.SourceCodeInfoExtension" + } + ]; +} +message UninterpretedOption { + repeated NamePart name = 2; + optional string identifier_value = 3; + optional uint64 positive_int_value = 4; + optional int64 negative_int_value = 5; + optional double double_value = 6; + optional bytes string_value = 7; + optional string aggregate_value = 8; + message NamePart { + required string name_part = 1; + required bool is_extension = 2; + } +} +enum Edition { + EDITION_UNKNOWN = 0; + EDITION_1_TEST_ONLY = 1; + EDITION_2_TEST_ONLY = 2; + EDITION_LEGACY = 900; + EDITION_PROTO2 = 998; + EDITION_PROTO3 = 999; + EDITION_2023 = 1000; + EDITION_2024 = 1001; + EDITION_99997_TEST_ONLY = 99997; + EDITION_99998_TEST_ONLY = 99998; + EDITION_99999_TEST_ONLY = 99999; + EDITION_MAX = 2147483647; +} +-- options.proto -- +syntax = "proto3"; +import "google/protobuf/descriptor.proto"; +message Files { + google.protobuf.FileDescriptorSet files = 1; +} +message UnusedOption { + string foo = 1; +} +message UsedOption { + string foo = 1; + extend google.protobuf.FileOptions { + UsedOption file_foo = 50000; + UnusedOption file_bar = 50001; + string file_baz = 50002; + } +} +extend google.protobuf.EnumOptions { + UsedOption enum_foo = 50000; + UnusedOption enum_bar = 50001; + string enum_baz = 50002; +} +extend google.protobuf.EnumValueOptions { + UsedOption enum_value_foo = 50000; + UnusedOption enum_value_bar = 50001; + string enum_value_baz = 50002; +} +extend google.protobuf.FieldOptions { + UsedOption field_foo = 50000; + UnusedOption field_bar = 50001; + string field_baz = 50002; +} +extend google.protobuf.MessageOptions { + UsedOption message_foo = 50000; + UnusedOption message_bar = 50001; + string message_baz = 50002; +} +extend google.protobuf.MethodOptions { + UsedOption method_foo = 50000; + UnusedOption method_bar = 50001; + string method_baz = 50002; +} +extend google.protobuf.OneofOptions { + UsedOption oneof_foo = 50000; + UnusedOption oneof_bar = 50001; + string oneof_baz = 50002; +} +extend google.protobuf.ServiceOptions { + UsedOption service_foo = 50000; + UnusedOption service_bar = 50001; + string service_baz = 50002; +} diff --git a/private/bufpkg/bufimage/bufimageutil/testdata/options/pkg.FooService.mixed.txtar b/private/bufpkg/bufimage/bufimageutil/testdata/options/pkg.FooService.mixed.txtar new file mode 100644 index 0000000000..8c996c168c --- /dev/null +++ b/private/bufpkg/bufimage/bufimageutil/testdata/options/pkg.FooService.mixed.txtar @@ -0,0 +1,213 @@ +-- a.proto -- +syntax = "proto2"; +package pkg; +import "options.proto"; +option (UsedOption.file_baz) = "str"; +option (UsedOption.file_foo) = { foo: "str" }; +message Empty { +} +service FooService { + option (service_baz) = "str"; + option (service_foo) = { foo: "str" }; + rpc DoNot ( Empty ) returns ( Empty ) { + option (method_baz) = "str"; + option (method_foo) = { foo: "str" }; + } +} +-- google/protobuf/descriptor.proto -- +syntax = "proto2"; +package google.protobuf; +option cc_enable_arenas = true; +option csharp_namespace = "Google.Protobuf.Reflection"; +option go_package = "google.golang.org/protobuf/types/descriptorpb"; +option java_outer_classname = "DescriptorProtos"; +option java_package = "com.google.protobuf"; +option objc_class_prefix = "GPB"; +option optimize_for = SPEED; +message FeatureSet { + optional FieldPresence field_presence = 1 [ + edition_defaults = { value: "EXPLICIT", edition: EDITION_LEGACY }, + edition_defaults = { value: "IMPLICIT", edition: EDITION_PROTO3 }, + edition_defaults = { value: "EXPLICIT", edition: EDITION_2023 }, + feature_support = { edition_introduced: EDITION_2023 }, + retention = RETENTION_RUNTIME, + targets = TARGET_TYPE_FIELD, + targets = TARGET_TYPE_FILE + ]; + optional EnumType enum_type = 2 [ + edition_defaults = { value: "CLOSED", edition: EDITION_LEGACY }, + edition_defaults = { value: "OPEN", edition: EDITION_PROTO3 }, + feature_support = { edition_introduced: EDITION_2023 }, + retention = RETENTION_RUNTIME, + targets = TARGET_TYPE_ENUM, + targets = TARGET_TYPE_FILE + ]; + optional RepeatedFieldEncoding repeated_field_encoding = 3 [ + edition_defaults = { value: "EXPANDED", edition: EDITION_LEGACY }, + edition_defaults = { value: "PACKED", edition: EDITION_PROTO3 }, + feature_support = { edition_introduced: EDITION_2023 }, + retention = RETENTION_RUNTIME, + targets = TARGET_TYPE_FIELD, + targets = TARGET_TYPE_FILE + ]; + optional Utf8Validation utf8_validation = 4 [ + edition_defaults = { value: "NONE", edition: EDITION_LEGACY }, + edition_defaults = { value: "VERIFY", edition: EDITION_PROTO3 }, + feature_support = { edition_introduced: EDITION_2023 }, + retention = RETENTION_RUNTIME, + targets = TARGET_TYPE_FIELD, + targets = TARGET_TYPE_FILE + ]; + optional MessageEncoding message_encoding = 5 [ + edition_defaults = { value: "LENGTH_PREFIXED", edition: EDITION_LEGACY }, + feature_support = { edition_introduced: EDITION_2023 }, + retention = RETENTION_RUNTIME, + targets = TARGET_TYPE_FIELD, + targets = TARGET_TYPE_FILE + ]; + optional JsonFormat json_format = 6 [ + edition_defaults = { value: "LEGACY_BEST_EFFORT", edition: EDITION_LEGACY }, + edition_defaults = { value: "ALLOW", edition: EDITION_PROTO3 }, + feature_support = { edition_introduced: EDITION_2023 }, + retention = RETENTION_RUNTIME, + targets = TARGET_TYPE_MESSAGE, + targets = TARGET_TYPE_ENUM, + targets = TARGET_TYPE_FILE + ]; + enum EnumType { + ENUM_TYPE_UNKNOWN = 0; + OPEN = 1; + CLOSED = 2; + } + enum FieldPresence { + FIELD_PRESENCE_UNKNOWN = 0; + EXPLICIT = 1; + IMPLICIT = 2; + LEGACY_REQUIRED = 3; + } + enum JsonFormat { + JSON_FORMAT_UNKNOWN = 0; + ALLOW = 1; + LEGACY_BEST_EFFORT = 2; + } + enum MessageEncoding { + MESSAGE_ENCODING_UNKNOWN = 0; + LENGTH_PREFIXED = 1; + DELIMITED = 2; + } + enum RepeatedFieldEncoding { + REPEATED_FIELD_ENCODING_UNKNOWN = 0; + PACKED = 1; + EXPANDED = 2; + } + enum Utf8Validation { + UTF8_VALIDATION_UNKNOWN = 0; + VERIFY = 2; + NONE = 3; + reserved 1; + } + extensions 1000 to 9994 [ + declaration = { + number: 1000, + full_name: ".pb.cpp", + type: ".pb.CppFeatures" + }, + declaration = { + number: 1001, + full_name: ".pb.java", + type: ".pb.JavaFeatures" + }, + declaration = { + number: 1002, + full_name: ".pb.go", + type: ".pb.GoFeatures" + }, + declaration = { + number: 9990, + full_name: ".pb.proto1", + type: ".pb.Proto1Features" + } + ]; + extensions 9995 to 9999, 10000; + reserved 999; +} +message FileOptions { + optional string java_package = 1; + optional string java_outer_classname = 8; + optional OptimizeMode optimize_for = 9 [default = SPEED]; + optional bool java_multiple_files = 10 [default = false]; + optional string go_package = 11; + optional bool cc_generic_services = 16 [default = false]; + optional bool java_generic_services = 17 [default = false]; + optional bool py_generic_services = 18 [default = false]; + optional bool java_generate_equals_and_hash = 20 [deprecated = true]; + optional bool deprecated = 23 [default = false]; + optional bool java_string_check_utf8 = 27 [default = false]; + optional bool cc_enable_arenas = 31 [default = true]; + optional string objc_class_prefix = 36; + optional string csharp_namespace = 37; + optional string swift_prefix = 39; + optional string php_class_prefix = 40; + optional string php_namespace = 41; + optional string php_metadata_namespace = 44; + optional string ruby_package = 45; + optional FeatureSet features = 50; + repeated UninterpretedOption uninterpreted_option = 999; + enum OptimizeMode { + SPEED = 1; + CODE_SIZE = 2; + LITE_RUNTIME = 3; + } + extensions 1000 to max; + reserved 38, 42; + reserved "php_generic_services"; +} +message MethodOptions { + optional bool deprecated = 33 [default = false]; + optional IdempotencyLevel idempotency_level = 34 [default = IDEMPOTENCY_UNKNOWN]; + optional FeatureSet features = 35; + repeated UninterpretedOption uninterpreted_option = 999; + enum IdempotencyLevel { + IDEMPOTENCY_UNKNOWN = 0; + NO_SIDE_EFFECTS = 1; + IDEMPOTENT = 2; + } + extensions 1000 to max; +} +message ServiceOptions { + optional bool deprecated = 33 [default = false]; + optional FeatureSet features = 34; + repeated UninterpretedOption uninterpreted_option = 999; + extensions 1000 to max; +} +message UninterpretedOption { + repeated NamePart name = 2; + optional string identifier_value = 3; + optional uint64 positive_int_value = 4; + optional int64 negative_int_value = 5; + optional double double_value = 6; + optional bytes string_value = 7; + optional string aggregate_value = 8; + message NamePart { + required string name_part = 1; + required bool is_extension = 2; + } +} +-- options.proto -- +syntax = "proto3"; +import "google/protobuf/descriptor.proto"; +message UsedOption { + string foo = 1; + extend google.protobuf.FileOptions { + UsedOption file_foo = 50000; + string file_baz = 50002; + } +} +extend google.protobuf.MethodOptions { + UsedOption method_foo = 50000; + string method_baz = 50002; +} +extend google.protobuf.ServiceOptions { + UsedOption service_foo = 50000; + string service_baz = 50002; +} diff --git a/private/pkg/slicesext/slicesext.go b/private/pkg/slicesext/slicesext.go index 57e0219d62..2abd2c5e61 100644 --- a/private/pkg/slicesext/slicesext.go +++ b/private/pkg/slicesext/slicesext.go @@ -244,6 +244,11 @@ func ToIndexedValuesMap[K comparable, V any](values []V, f func(V) K) map[K][]In return ToValuesMap(ToIndexed(values), func(indexedV Indexed[V]) K { return f(indexedV.Value) }) } +// ToIndexedValuesMapError calls ToValuesMapError on the indexed values. +func ToIndexedValuesMapError[K comparable, V any](values []V, f func(V) (K, error)) (map[K][]Indexed[V], error) { + return ToValuesMapError(ToIndexed(values), func(indexedV Indexed[V]) (K, error) { return f(indexedV.Value) }) +} + // ToUniqueIndexedValuesMap calls ToUniqueValuesMap on the indexed values. func ToUniqueIndexedValuesMap[K comparable, V any](values []V, f func(V) K) (map[K]Indexed[V], error) { return ToUniqueValuesMap(ToIndexed(values), func(indexedV Indexed[V]) K { return f(indexedV.Value) })