From a0a21cae5271c4b5c88f3ee870fb266766f36721 Mon Sep 17 00:00:00 2001 From: Oliver Sun Date: Tue, 3 Nov 2026 14:41:13 -0500 Subject: [PATCH 01/16] Filter imports last --- cmd/buf/buf_test.go | 38 +++++++++++++++++++ .../testdata/imports/success/sibling/a.proto | 7 ++++ .../testdata/imports/success/sibling/b.proto | 3 ++ private/buf/bufctl/controller.go | 8 ++-- 4 files changed, 53 insertions(+), 3 deletions(-) create mode 100644 cmd/buf/testdata/imports/success/sibling/a.proto create mode 100644 cmd/buf/testdata/imports/success/sibling/b.proto diff --git a/cmd/buf/buf_test.go b/cmd/buf/buf_test.go index 92d97fa3d4..a229c72671 100644 --- a/cmd/buf/buf_test.go +++ b/cmd/buf/buf_test.go @@ -2559,6 +2559,44 @@ func TestBuildOverlappingPaths(t *testing.T) { ) } +func TestBuildTypeWithExcludeImports(t *testing.T) { + t.Parallel() + testBuildLsFilesFormatImport( + t, + 0, + []string{"a.proto"}, + filepath.Join("testdata", "imports", "success", "wkt"), + "--type", + "A", + "--exclude-imports", + ) +} + +func TestBuildPathWithExcludeImportsFromImage(t *testing.T) { + t.Parallel() + tempDir := t.TempDir() + imagePath := filepath.Join(tempDir, "image.binpb") + testRunStdout( + t, + nil, + 0, + ``, + "build", + filepath.Join("testdata", "imports", "success", "sibling"), + "-o", + imagePath, + ) + testBuildLsFilesFormatImport( + t, + 0, + []string{"a.proto"}, + imagePath, + "--path", + "a.proto", + "--exclude-imports", + ) +} + func TestExportProto(t *testing.T) { t.Parallel() tempDir := t.TempDir() diff --git a/cmd/buf/testdata/imports/success/sibling/a.proto b/cmd/buf/testdata/imports/success/sibling/a.proto new file mode 100644 index 0000000000..9d7f6385be --- /dev/null +++ b/cmd/buf/testdata/imports/success/sibling/a.proto @@ -0,0 +1,7 @@ +syntax = "proto3"; + +import "b.proto"; + +message A { + B b = 1; +} diff --git a/cmd/buf/testdata/imports/success/sibling/b.proto b/cmd/buf/testdata/imports/success/sibling/b.proto new file mode 100644 index 0000000000..9dd1c573b4 --- /dev/null +++ b/cmd/buf/testdata/imports/success/sibling/b.proto @@ -0,0 +1,3 @@ +syntax = "proto3"; + +message B {} diff --git a/private/buf/bufctl/controller.go b/private/buf/bufctl/controller.go index cdc2078cd9..fd7094ac6c 100644 --- a/private/buf/bufctl/controller.go +++ b/private/buf/bufctl/controller.go @@ -1435,9 +1435,6 @@ func filterImage( ) (bufimage.Image, error) { newImage := image var err error - if functionOptions.imageExcludeImports { - newImage = bufimage.ImageWithoutImports(newImage) - } includeTypes := functionOptions.imageIncludeTypes excludeTypes := functionOptions.imageExcludeTypes if len(includeTypes) > 0 || len(excludeTypes) > 0 { @@ -1474,6 +1471,11 @@ func filterImage( } } } + // Exclude imports last because the type filter (--type) and the path filter (--path) above can + // keep or re-add files as imports. + if functionOptions.imageExcludeImports { + newImage = bufimage.ImageWithoutImports(newImage) + } return newImage, nil } From 0a8558474f8969fe5fee6b430083f4741b1f5db5 Mon Sep 17 00:00:00 2001 From: Oliver Sun Date: Fri, 6 Nov 2026 18:01:54 -0500 Subject: [PATCH 02/16] update image.proto --- private/gen/proto/go/buf/alpha/image/v1/image.pb.go | 6 ++++-- proto/buf/alpha/image/v1/image.proto | 6 ++++-- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/private/gen/proto/go/buf/alpha/image/v1/image.pb.go b/private/gen/proto/go/buf/alpha/image/v1/image.pb.go index 37c49a7946..fffcff01f5 100644 --- a/private/gen/proto/go/buf/alpha/image/v1/image.pb.go +++ b/private/gen/proto/go/buf/alpha/image/v1/image.pb.go @@ -610,11 +610,13 @@ type ImageFileExtension_builder struct { // is_import denotes whether this file is considered an "import". // - // An import is a file which was not derived from the local source files. - // There are two cases where this could be true: + // An import is a file that is not a target of the build. There are three + // cases where this is true: // // 1. A Well-Known Type included from the compiler. // 2. A file that was included from a Buf module dependency. + // 3. A file that is not itself a target, but is (possibly transitively) + // imported by a target file. // // We use "import" as this matches with the protoc concept of // --include_imports, however import is a bit of an overloaded term. diff --git a/proto/buf/alpha/image/v1/image.proto b/proto/buf/alpha/image/v1/image.proto index 1e15e1a59b..f87212fbe5 100644 --- a/proto/buf/alpha/image/v1/image.proto +++ b/proto/buf/alpha/image/v1/image.proto @@ -69,11 +69,13 @@ message ImageFile { message ImageFileExtension { // is_import denotes whether this file is considered an "import". // - // An import is a file which was not derived from the local source files. - // There are two cases where this could be true: + // An import is a file that is not a target of the build. There are three + // cases where this is true: // // 1. A Well-Known Type included from the compiler. // 2. A file that was included from a Buf module dependency. + // 3. A file that is not itself a target, but is (possibly transitively) + // imported by a target file. // // We use "import" as this matches with the protoc concept of // --include_imports, however import is a bit of an overloaded term. From 1f90430b9f7d8247b4eec0bc91a9894efa37300f Mon Sep 17 00:00:00 2001 From: Oliver Sun Date: Fri, 6 Nov 2026 18:15:16 -0500 Subject: [PATCH 03/16] FilterImage marks is_import based --type too --- .../bufimage/bufimageutil/bufimageutil.go | 19 ++++++- .../bufimageutil/bufimageutil_test.go | 51 +++++++++++++++++++ .../bufimage/bufimageutil/image_filter.go | 25 ++++++++- 3 files changed, 91 insertions(+), 4 deletions(-) diff --git a/private/bufpkg/bufimage/bufimageutil/bufimageutil.go b/private/bufpkg/bufimage/bufimageutil/bufimageutil.go index cc768d9230..6699ff547c 100644 --- a/private/bufpkg/bufimage/bufimageutil/bufimageutil.go +++ b/private/bufpkg/bufimage/bufimageutil/bufimageutil.go @@ -121,6 +121,14 @@ func WithAllowIncludeOfImportedType() ImageFilterOption { // "acme.foo", all of its sub-packages, and all of their types; "acme.Foo.**" // matches the message "acme.Foo" and all of its nested types. // +// A kept file that contains none of the included types is kept only because +// an included type references a type in it, and is marked as an import. Files +// that are already imports stay imports. Normally, if `--path a.proto` is passed +// it's an error to pass `--type B`, because they don't intersect. However, if +// `--path a.proto` and `--type pkg` is passed and both a.proto and b.proto are in +// the pkg package, then it's valid. This time `--type pkg` doesn't not make b.proto +// a target because it's excluded by `--path a.proto` already. +// // If the type does not exist in the image, an error wrapping // [ErrImageFilterTypeNotFound] will be returned. func WithIncludeTypes(typeNames ...string) ImageFilterOption { @@ -290,6 +298,10 @@ type transitiveClosure struct { // The set of imports for each file. This allows for re-writing imports // for files whose contents have been pruned. imports map[string]map[string]struct{} + // The paths of files that contain an explicitly included type or package. + // Every other file kept in the closure is present only because an included + // type references it, and is marked as an import. + includedFiles map[string]struct{} } type closureInclusionMode int @@ -315,8 +327,9 @@ const ( func newTransitiveClosure() *transitiveClosure { return &transitiveClosure{ - elements: map[namedDescriptor]closureInclusionMode{}, - imports: map[string]map[string]struct{}{}, + elements: map[namedDescriptor]closureInclusionMode{}, + imports: map[string]map[string]struct{}{}, + includedFiles: map[string]struct{}{}, } } @@ -380,6 +393,7 @@ func (t *transitiveClosure) includeType( if mode := t.elements[descriptorInfo.element]; mode == inclusionModeExcluded { return fmt.Errorf("inclusion of type %q: %w", typeName, ErrImageFilterTypeExcluded) } + t.includedFiles[descriptorInfo.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(), ".")) @@ -467,6 +481,7 @@ func (t *transitiveClosure) includePackage( if err := t.addElement(fileDescriptor, "", false, imageIndex, options); err != nil { return included, fmt.Errorf("inclusion of package %q: %w", pkg.fullName, err) } + t.includedFiles[file.Path()] = struct{}{} included++ } if recursive { diff --git a/private/bufpkg/bufimage/bufimageutil/bufimageutil_test.go b/private/bufpkg/bufimage/bufimageutil/bufimageutil_test.go index 944030420c..b1b84c86b3 100644 --- a/private/bufpkg/bufimage/bufimageutil/bufimageutil_test.go +++ b/private/bufpkg/bufimage/bufimageutil/bufimageutil_test.go @@ -676,6 +676,57 @@ extend google.protobuf.MessageOptions { string tag = 50000; } testOptionDependencies(t, nil, WithIncludeTypes("a.Foo"), WithExcludeTypes("opts.tag")) } +func TestIncludeTypesMarksImports(t *testing.T) { + t.Parallel() + moduleSet, err := bufmoduletesting.NewModuleSet(bufmoduletesting.ModuleData{ + PathToData: map[string][]byte{ + "a.proto": []byte(`syntax = "proto3"; +package a; +import "b.proto"; +message A { b.B b = 1; } +`), + "b.proto": []byte(`syntax = "proto3"; +package b; +message B {} +`), + }, + }) + require.NoError(t, err) + image, err := bufimage.BuildImage( + t.Context(), + slogtestext.NewLogger(t), + bufmodule.ModuleSetToModuleReadBucketWithOnlyProtoFiles(moduleSet), + bufimage.WithExcludeSourceCodeInfo(), + ) + require.NoError(t, err) + // Each case lists the expected IsImport value per file. + testIsImport := func(t *testing.T, want map[string]bool, opts ...ImageFilterOption) { + t.Helper() + _, filteredImage := runFilterImage(t, image, opts...) + got := make(map[string]bool, len(filteredImage.Files())) + for _, imageFile := range filteredImage.Files() { + got[imageFile.Path()] = imageFile.IsImport() + } + assert.Equal(t, want, got) + } + t.Run("referenced_file_becomes_import", func(t *testing.T) { + t.Parallel() + testIsImport(t, map[string]bool{"a.proto": false, "b.proto": true}, WithIncludeTypes("a.A")) + }) + t.Run("named_file_stays_non_import", func(t *testing.T) { + t.Parallel() + testIsImport(t, map[string]bool{"a.proto": false, "b.proto": false}, WithIncludeTypes("a.A", "b.B")) + }) + t.Run("package_include", func(t *testing.T) { + t.Parallel() + testIsImport(t, map[string]bool{"a.proto": false, "b.proto": true}, WithIncludeTypes("a")) + }) + t.Run("exclude_only_leaves_imports_unchanged", func(t *testing.T) { + t.Parallel() + testIsImport(t, map[string]bool{"a.proto": false, "b.proto": false}, WithExcludeTypes("a.A")) + }) +} + func TestEmptyFiles(t *testing.T) { t.Parallel() t.Run("include_empty_file", func(t *testing.T) { diff --git a/private/bufpkg/bufimage/bufimageutil/image_filter.go b/private/bufpkg/bufimage/bufimageutil/image_filter.go index 9f5705931f..aadd631948 100644 --- a/private/bufpkg/bufimage/bufimageutil/image_filter.go +++ b/private/bufpkg/bufimage/bufimageutil/image_filter.go @@ -169,11 +169,32 @@ func filterImageFile( if newFileDescriptor == nil { return nil, nil // Filtered out. } + // A file that contains no included type is kept only because an included + // type references it, so it becomes an import. Files that were already + // imports stay imports. + isImport := imageFile.IsImport() + if len(options.includeTypes) > 0 { + if _, ok := closure.includedFiles[imageFile.Path()]; !ok { + isImport = true + } + } if !changed { if len(sourcePathsRemap) > 0 { return nil, syserror.Newf("unexpected %d sourcePathsRemaps", len(sourcePathsRemap)) } - return imageFile, nil // No changes required. + if isImport == imageFile.IsImport() { + return imageFile, nil // No changes required. + } + return bufimage.NewImageFile( + fileDescriptor, + imageFile.FullName(), + imageFile.CommitID(), + imageFile.ExternalPath(), + imageFile.LocalPath(), + isImport, + imageFile.IsSyntaxUnspecified(), + imageFile.UnusedDependencyIndexes(), + ) } // Remap the source code info. @@ -206,7 +227,7 @@ func filterImageFile( imageFile.CommitID(), imageFile.ExternalPath(), imageFile.LocalPath(), - imageFile.IsImport(), + isImport, imageFile.IsSyntaxUnspecified(), nil, // There are no unused dependencies. ) From 100e184905c2519442a373627a654d44fb3d0925 Mon Sep 17 00:00:00 2001 From: Oliver Sun Date: Fri, 6 Nov 2026 18:20:09 -0500 Subject: [PATCH 04/16] apply --path filter before --type filter for non-workspace inputs --- cmd/buf/buf_test.go | 98 ++++++++++++++++++++++++++++++++ private/buf/bufctl/controller.go | 32 ++++++----- 2 files changed, 117 insertions(+), 13 deletions(-) diff --git a/cmd/buf/buf_test.go b/cmd/buf/buf_test.go index a229c72671..74483c725c 100644 --- a/cmd/buf/buf_test.go +++ b/cmd/buf/buf_test.go @@ -2597,6 +2597,104 @@ func TestBuildPathWithExcludeImportsFromImage(t *testing.T) { ) } +func TestBuildTypeMarksReferencedFileAsImport(t *testing.T) { + t.Parallel() + // a.proto imports b.proto from the same module. Including only A keeps + // b.proto for B, but marks it as an import because it contains no + // included type, so --exclude-imports drops it. + testBuildLsFilesFormatImport( + t, + 0, + []string{"a.proto"}, + filepath.Join("testdata", "imports", "success", "sibling"), + "--type", + "A", + "--exclude-imports", + ) +} + +func TestBuildTypeWithPathMarksReferencedFileAsImport(t *testing.T) { + t.Parallel() + // --path lists b.proto, but --type includes no type defined in it, so + // b.proto is an import and --exclude-imports drops it. + testBuildLsFilesFormatImport( + t, + 0, + []string{"a.proto"}, + filepath.Join("testdata", "imports", "success", "sibling"), + "--type", + "A", + "--path", + filepath.Join("testdata", "imports", "success", "sibling", "a.proto"), + "--path", + filepath.Join("testdata", "imports", "success", "sibling", "b.proto"), + "--exclude-imports", + ) +} + +func TestBuildTypeWithPathMarksReferencedFileAsImportFromImage(t *testing.T) { + t.Parallel() + // Same flags as TestBuildTypeWithPathMarksReferencedFileAsImport, with an + // image input. The path filter must run before the type filter, as it does + // for a directory input, so that it does not reset the import flag that the + // type filter set on b.proto. + tempDir := t.TempDir() + imagePath := filepath.Join(tempDir, "image.binpb") + testRunStdout( + t, + nil, + 0, + ``, + "build", + filepath.Join("testdata", "imports", "success", "sibling"), + "-o", + imagePath, + ) + testBuildLsFilesFormatImport( + t, + 0, + []string{"a.proto"}, + imagePath, + "--type", + "A", + "--path", + "a.proto", + "--path", + "b.proto", + "--exclude-imports", + ) +} + +func TestBuildTypeOutsidePathFromImage(t *testing.T) { + t.Parallel() + // --path keeps only b.proto, so --type A is not found, as for a directory + // input. + tempDir := t.TempDir() + imagePath := filepath.Join(tempDir, "image.binpb") + testRunStdout( + t, + nil, + 0, + ``, + "build", + filepath.Join("testdata", "imports", "success", "sibling"), + "-o", + imagePath, + ) + testRunStderrContainsNoWarn( + t, + nil, + 1, + []string{`inclusion of type "A": not found`}, + "build", + imagePath, + "--path", + "b.proto", + "--type", + "A", + ) +} + func TestExportProto(t *testing.T) { t.Parallel() tempDir := t.TempDir() diff --git a/private/buf/bufctl/controller.go b/private/buf/bufctl/controller.go index fd7094ac6c..923ce0ac58 100644 --- a/private/buf/bufctl/controller.go +++ b/private/buf/bufctl/controller.go @@ -1435,19 +1435,12 @@ func filterImage( ) (bufimage.Image, error) { newImage := image var err error - 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 - } - } + // Filter by path before filtering by type. For a workspace input, the target + // paths were applied when the workspace was read, so the type filter already + // runs after them. Running the path filter first for other inputs keeps both + // input kinds consistent. The order matters because the type filter marks + // files that contain no included type as imports, and the path filter + // resets the import flag of every file that matches --path. if !imageCameFromAWorkspace { if len(functionOptions.targetPaths) > 0 || len(functionOptions.targetExcludePaths) > 0 { // bufimage expects normalized paths, so we need to normalize the paths @@ -1471,6 +1464,19 @@ func filterImage( } } } + 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 + } + } // Exclude imports last because the type filter (--type) and the path filter (--path) above can // keep or re-add files as imports. if functionOptions.imageExcludeImports { From 8d5c42a8be58104c0b0fb8d3fe09537199a57c05 Mon Sep 17 00:00:00 2001 From: Oliver Sun Date: Fri, 6 Nov 2026 18:20:56 -0500 Subject: [PATCH 05/16] update test and cmd description --- cmd/buf/internal/command/build/build.go | 2 +- cmd/buf/internal/command/generate/generate.go | 2 +- cmd/buf/internal/command/generate/generate_test.go | 3 --- 3 files changed, 2 insertions(+), 5 deletions(-) diff --git a/cmd/buf/internal/command/build/build.go b/cmd/buf/internal/command/build/build.go index f40b186a72..ff0f6e2166 100644 --- a/cmd/buf/internal/command/build/build.go +++ b/cmd/buf/internal/command/build/build.go @@ -132,7 +132,7 @@ func (f *flags) Bind(flagSet *pflag.FlagSet) { &f.Types, typeFlagName, 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. A type name may end with \".**\" to recursively include the named element and everything nested beneath it, such as a package and all of its sub-packages", + "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. A type name may end with \".**\" to recursively include the named element and everything nested beneath it, such as a package and all of its sub-packages. A file kept only because an included type references a type in it is marked as an import", ) } diff --git a/cmd/buf/internal/command/generate/generate.go b/cmd/buf/internal/command/generate/generate.go index 7b7dd31681..5709fbdced 100644 --- a/cmd/buf/internal/command/generate/generate.go +++ b/cmd/buf/internal/command/generate/generate.go @@ -487,7 +487,7 @@ func (f *flags) Bind(flagSet *pflag.FlagSet) { &f.Types, typeFlagName, 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. A type name may end with \".**\" to recursively include the named element and everything nested beneath it, such as a package and all of its sub-packages. Flag usage overrides buf.gen.yaml", + "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. A type name may end with \".**\" to recursively include the named element and everything nested beneath it, such as a package and all of its sub-packages. A file kept only because an included type references a type in it is marked as an import. Flag usage overrides buf.gen.yaml", ) flagSet.StringSliceVar( &f.TypesDeprecated, diff --git a/cmd/buf/internal/command/generate/generate_test.go b/cmd/buf/internal/command/generate/generate_test.go index 007d2d3a2a..f712a2e931 100644 --- a/cmd/buf/internal/command/generate/generate_test.go +++ b/cmd/buf/internal/command/generate/generate_test.go @@ -322,9 +322,6 @@ inputs: 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", From fdd090013f1d5a053e541bc60ed5f32d4ddd6419 Mon Sep 17 00:00:00 2001 From: Oliver Sun Date: Fri, 6 Nov 2026 18:21:42 -0500 Subject: [PATCH 06/16] update comment --- private/buf/bufctl/controller.go | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/private/buf/bufctl/controller.go b/private/buf/bufctl/controller.go index 923ce0ac58..1690d634af 100644 --- a/private/buf/bufctl/controller.go +++ b/private/buf/bufctl/controller.go @@ -1435,12 +1435,14 @@ func filterImage( ) (bufimage.Image, error) { newImage := image var err error - // Filter by path before filtering by type. For a workspace input, the target - // paths were applied when the workspace was read, so the type filter already - // runs after them. Running the path filter first for other inputs keeps both - // input kinds consistent. The order matters because the type filter marks - // files that contain no included type as imports, and the path filter - // resets the import flag of every file that matches --path. + // Filter by path before filtering by type. + // - For a workspace input, the target paths were applied when the workspace + // was read, so the type filter already runs after them. + // - For a non-workspace input, we need to apply the path filters first too to + // stay consistent. + // The order matters because the type filter marks files that contain no + // included type as imports, and the path filter resets the import flag of every + // file that matches --path. if !imageCameFromAWorkspace { if len(functionOptions.targetPaths) > 0 || len(functionOptions.targetExcludePaths) > 0 { // bufimage expects normalized paths, so we need to normalize the paths From dbebb79ebb85c377bb4b31f7dd9d197ba301689d Mon Sep 17 00:00:00 2001 From: Oliver Sun Date: Fri, 6 Nov 2026 18:22:35 -0500 Subject: [PATCH 07/16] indentation --- private/buf/bufctl/controller.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/private/buf/bufctl/controller.go b/private/buf/bufctl/controller.go index 1690d634af..cdb72ed333 100644 --- a/private/buf/bufctl/controller.go +++ b/private/buf/bufctl/controller.go @@ -1437,7 +1437,7 @@ func filterImage( var err error // Filter by path before filtering by type. // - For a workspace input, the target paths were applied when the workspace - // was read, so the type filter already runs after them. + // was read, so the type filter already runs after them. // - For a non-workspace input, we need to apply the path filters first too to // stay consistent. // The order matters because the type filter marks files that contain no From 4f054f9b34769003838914c6dd5721dd72387720 Mon Sep 17 00:00:00 2001 From: Oliver Sun Date: Fri, 6 Nov 2026 18:27:52 -0500 Subject: [PATCH 08/16] changelog --- CHANGELOG.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2150c5ea62..710d7e5434 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,16 @@ - Fix LSP hover and highlighting of CEL expressions whose proto strings contain escape sequences, which placed the ranges of string literals, and of tokens after a `\u` or `\U` escape, wrongly. +- Fix a bug where files that `--type` kept only because a selected type references them were + not counted as imports. `buf build` and `buf generate` now treat them as imports, and + `buf build --exclude-imports` removes imports after `--path` and `--type` are applied. The + `is_import` comment in `image.proto` now says which files are imports. In practice: + - `buf build --type a.A --exclude-imports` outputs only the file that defines `a.A`, and no + longer fails when `a.A` references a type from a dependency. + - `buf generate` with `types` no longer generates code for files kept only by reference. To + generate them again, set `include_imports: true` on the plugin or add their types. + - `--path` now gives the same result for an image input as for a source input, with or without + `--type`. ## [v1.75.0] - 2026-10-21 From 00571216eef2e28b7b4d0975ec569fe00f243fca Mon Sep 17 00:00:00 2001 From: Oliver Sun Date: Mon, 9 Nov 2026 09:55:24 +0100 Subject: [PATCH 09/16] drop extra sentence --- cmd/buf/internal/command/build/build.go | 2 +- cmd/buf/internal/command/generate/generate.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/cmd/buf/internal/command/build/build.go b/cmd/buf/internal/command/build/build.go index ff0f6e2166..f40b186a72 100644 --- a/cmd/buf/internal/command/build/build.go +++ b/cmd/buf/internal/command/build/build.go @@ -132,7 +132,7 @@ func (f *flags) Bind(flagSet *pflag.FlagSet) { &f.Types, typeFlagName, 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. A type name may end with \".**\" to recursively include the named element and everything nested beneath it, such as a package and all of its sub-packages. A file kept only because an included type references a type in it is marked as an import", + "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. A type name may end with \".**\" to recursively include the named element and everything nested beneath it, such as a package and all of its sub-packages", ) } diff --git a/cmd/buf/internal/command/generate/generate.go b/cmd/buf/internal/command/generate/generate.go index 5709fbdced..7b7dd31681 100644 --- a/cmd/buf/internal/command/generate/generate.go +++ b/cmd/buf/internal/command/generate/generate.go @@ -487,7 +487,7 @@ func (f *flags) Bind(flagSet *pflag.FlagSet) { &f.Types, typeFlagName, 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. A type name may end with \".**\" to recursively include the named element and everything nested beneath it, such as a package and all of its sub-packages. A file kept only because an included type references a type in it is marked as an import. Flag usage overrides buf.gen.yaml", + "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. A type name may end with \".**\" to recursively include the named element and everything nested beneath it, such as a package and all of its sub-packages. Flag usage overrides buf.gen.yaml", ) flagSet.StringSliceVar( &f.TypesDeprecated, From 0dbfdb001ac5a1553d42b4f09082253c9e6da111 Mon Sep 17 00:00:00 2001 From: Oliver Sun Date: Mon, 9 Nov 2026 10:43:34 +0100 Subject: [PATCH 10/16] changelog --- CHANGELOG.md | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 710d7e5434..15874b8d9e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,12 +4,13 @@ - Fix LSP hover and highlighting of CEL expressions whose proto strings contain escape sequences, which placed the ranges of string literals, and of tokens after a `\u` or `\U` escape, wrongly. -- Fix a bug where files that `--type` kept only because a selected type references them were - not counted as imports. `buf build` and `buf generate` now treat them as imports, and - `buf build --exclude-imports` removes imports after `--path` and `--type` are applied. The - `is_import` comment in `image.proto` now says which files are imports. In practice: - - `buf build --type a.A --exclude-imports` outputs only the file that defines `a.A`, and no - longer fails when `a.A` references a type from a dependency. +- Fix a bug where a file imported by the file of a type specified by `--type`, without defining a + specified type itself, was not considered an import. The `is_import` comment in `image.proto` is + also updated to describe this. Image filtering now has a unified order across commands, where + the path filters apply before the type filters and `--exclude-imports` applies last. Some + consequences: + - `buf build --type a.A --exclude-imports` outputs only the file that defines `a.A`, and no longer + fails when `a.A` has a field whose type is defined in a dependency or a Well-Known Type. - `buf generate` with `types` no longer generates code for files kept only by reference. To generate them again, set `include_imports: true` on the plugin or add their types. - `--path` now gives the same result for an image input as for a source input, with or without From 9149c0238aab36db8c15857af3c939ba90031514 Mon Sep 17 00:00:00 2001 From: Oliver Sun Date: Mon, 9 Nov 2026 11:04:31 +0100 Subject: [PATCH 11/16] update comment --- cmd/buf/buf_test.go | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/cmd/buf/buf_test.go b/cmd/buf/buf_test.go index 74483c725c..7318e0d0f1 100644 --- a/cmd/buf/buf_test.go +++ b/cmd/buf/buf_test.go @@ -2615,8 +2615,7 @@ func TestBuildTypeMarksReferencedFileAsImport(t *testing.T) { func TestBuildTypeWithPathMarksReferencedFileAsImport(t *testing.T) { t.Parallel() - // --path lists b.proto, but --type includes no type defined in it, so - // b.proto is an import and --exclude-imports drops it. + // Image building/filtering takes the intersection of `--path` and `--type`. testBuildLsFilesFormatImport( t, 0, @@ -2634,10 +2633,7 @@ func TestBuildTypeWithPathMarksReferencedFileAsImport(t *testing.T) { func TestBuildTypeWithPathMarksReferencedFileAsImportFromImage(t *testing.T) { t.Parallel() - // Same flags as TestBuildTypeWithPathMarksReferencedFileAsImport, with an - // image input. The path filter must run before the type filter, as it does - // for a directory input, so that it does not reset the import flag that the - // type filter set on b.proto. + // Same flags as TestBuildTypeWithPathMarksReferencedFileAsImport, but with image input. tempDir := t.TempDir() imagePath := filepath.Join(tempDir, "image.binpb") testRunStdout( @@ -2667,8 +2663,7 @@ func TestBuildTypeWithPathMarksReferencedFileAsImportFromImage(t *testing.T) { func TestBuildTypeOutsidePathFromImage(t *testing.T) { t.Parallel() - // --path keeps only b.proto, so --type A is not found, as for a directory - // input. + // It's an error when the intersection between `--path` and `--type` is empty. tempDir := t.TempDir() imagePath := filepath.Join(tempDir, "image.binpb") testRunStdout( From c5bdc459b727332289ff4aff441389ba811367cc Mon Sep 17 00:00:00 2001 From: Oliver Sun Date: Tue, 10 Nov 2026 12:00:27 +0100 Subject: [PATCH 12/16] Update behavior when WithAllowIncludeOfImportedType is set --- cmd/buf/buf_test.go | 15 +++++ .../testdata/imports/success/samepkg/a.proto | 9 +++ .../testdata/imports/success/samepkg/b.proto | 5 ++ .../bufimage/bufimageutil/bufimageutil.go | 16 +++-- .../bufimageutil/bufimageutil_test.go | 63 +++++++++++++++++++ .../bufimage/bufimageutil/image_filter.go | 9 ++- .../proto/go/buf/alpha/image/v1/image.pb.go | 4 ++ proto/buf/alpha/image/v1/image.proto | 4 ++ 8 files changed, 118 insertions(+), 7 deletions(-) create mode 100644 cmd/buf/testdata/imports/success/samepkg/a.proto create mode 100644 cmd/buf/testdata/imports/success/samepkg/b.proto diff --git a/cmd/buf/buf_test.go b/cmd/buf/buf_test.go index 7318e0d0f1..bc3fb09d84 100644 --- a/cmd/buf/buf_test.go +++ b/cmd/buf/buf_test.go @@ -2690,6 +2690,21 @@ func TestBuildTypeOutsidePathFromImage(t *testing.T) { ) } +func TestBuildPackageTypeWithPathKeepsIntersection(t *testing.T) { + t.Parallel() + testBuildLsFilesFormatImport( + t, + 0, + []string{"a.proto"}, + filepath.Join("testdata", "imports", "success", "samepkg"), + "--type", + "pkg", + "--path", + filepath.Join("testdata", "imports", "success", "samepkg", "a.proto"), + "--exclude-imports", + ) +} + func TestExportProto(t *testing.T) { t.Parallel() tempDir := t.TempDir() diff --git a/cmd/buf/testdata/imports/success/samepkg/a.proto b/cmd/buf/testdata/imports/success/samepkg/a.proto new file mode 100644 index 0000000000..810cf4780c --- /dev/null +++ b/cmd/buf/testdata/imports/success/samepkg/a.proto @@ -0,0 +1,9 @@ +syntax = "proto3"; + +package pkg; + +import "b.proto"; + +message A { + B b = 1; +} diff --git a/cmd/buf/testdata/imports/success/samepkg/b.proto b/cmd/buf/testdata/imports/success/samepkg/b.proto new file mode 100644 index 0000000000..ca491858ac --- /dev/null +++ b/cmd/buf/testdata/imports/success/samepkg/b.proto @@ -0,0 +1,5 @@ +syntax = "proto3"; + +package pkg; + +message B {} diff --git a/private/bufpkg/bufimage/bufimageutil/bufimageutil.go b/private/bufpkg/bufimage/bufimageutil/bufimageutil.go index 6699ff547c..41dfa46cba 100644 --- a/private/bufpkg/bufimage/bufimageutil/bufimageutil.go +++ b/private/bufpkg/bufimage/bufimageutil/bufimageutil.go @@ -103,6 +103,10 @@ func WithExcludeKnownExtensions() ImageFilterOption { // 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. // Excluded types are always allowed to be in imported files or modules. +// +// A file that defines an included type, named directly or through its package, +// is marked as a non-import, since it was selected, even if it was an import +// before the type filter is applied. func WithAllowIncludeOfImportedType() ImageFilterOption { return func(opts *imageFilterOptions) { opts.allowImportedTypes = true @@ -123,11 +127,13 @@ func WithAllowIncludeOfImportedType() ImageFilterOption { // // A kept file that contains none of the included types is kept only because // an included type references a type in it, and is marked as an import. Files -// that are already imports stay imports. Normally, if `--path a.proto` is passed -// it's an error to pass `--type B`, because they don't intersect. However, if -// `--path a.proto` and `--type pkg` is passed and both a.proto and b.proto are in -// the pkg package, then it's valid. This time `--type pkg` doesn't not make b.proto -// a target because it's excluded by `--path a.proto` already. +// that are already imports stay imports, unless [WithAllowIncludeOfImportedType] +// is set and the file defines an included type. +// +// Normally, if `--path a.proto` is passed it's an error to pass `--type B`, because +// they don't intersect. However, if `--path a.proto` and `--type pkg` is passed and +// both a.proto and b.proto are in the pkg package, then it's valid. This time `--type pkg` +// does not make b.proto a target because it's excluded by `--path a.proto` already. // // If the type does not exist in the image, an error wrapping // [ErrImageFilterTypeNotFound] will be returned. diff --git a/private/bufpkg/bufimage/bufimageutil/bufimageutil_test.go b/private/bufpkg/bufimage/bufimageutil/bufimageutil_test.go index b1b84c86b3..ebd7f3bba2 100644 --- a/private/bufpkg/bufimage/bufimageutil/bufimageutil_test.go +++ b/private/bufpkg/bufimage/bufimageutil/bufimageutil_test.go @@ -727,6 +727,69 @@ message B {} }) } +func TestAllowIncludeOfImportedTypeMarksTargets(t *testing.T) { + t.Parallel() + moduleSet, err := bufmoduletesting.NewOmniProvider( + bufmoduletesting.ModuleData{ + Name: "buf.build/repo/main", + PathToData: map[string][]byte{ + "a.proto": []byte(`syntax = "proto3"; +package a; +import "b.proto"; +message A { b.B b = 1; } +`), + }, + }, + bufmoduletesting.ModuleData{ + Name: "buf.build/repo/dep", + PathToData: map[string][]byte{ + "b.proto": []byte(`syntax = "proto3"; +package b; +message B {} +`), + }, + NotTargeted: true, + }, + ) + require.NoError(t, err) + image, err := bufimage.BuildImage( + t.Context(), + slogtestext.NewLogger(t), + bufmodule.ModuleSetToModuleReadBucketWithOnlyProtoFiles(moduleSet), + bufimage.WithExcludeSourceCodeInfo(), + ) + require.NoError(t, err) + + require.False(t, image.GetFile("a.proto").IsImport()) + require.True(t, image.GetFile("b.proto").IsImport()) + + testIsImportAfterFilter := func(t *testing.T, want map[string]bool, opts ...ImageFilterOption) { + t.Helper() + _, filteredImage := runFilterImage(t, image, append(opts, WithAllowIncludeOfImportedType())...) + got := make(map[string]bool, len(filteredImage.Files())) + for _, imageFile := range filteredImage.Files() { + got[imageFile.Path()] = imageFile.IsImport() + } + assert.Equal(t, want, got) + } + t.Run("named_dependency_type_becomes_target", func(t *testing.T) { + t.Parallel() + testIsImportAfterFilter(t, map[string]bool{"b.proto": false}, WithIncludeTypes("b.B")) + }) + t.Run("named_dependency_package_becomes_target", func(t *testing.T) { + t.Parallel() + testIsImportAfterFilter(t, map[string]bool{"b.proto": false}, WithIncludeTypes("b")) + }) + t.Run("referenced_dependency_file_stays_import", func(t *testing.T) { + t.Parallel() + testIsImportAfterFilter(t, map[string]bool{"a.proto": false, "b.proto": true}, WithIncludeTypes("a.A")) + }) + t.Run("both_named", func(t *testing.T) { + t.Parallel() + testIsImportAfterFilter(t, map[string]bool{"a.proto": false, "b.proto": false}, WithIncludeTypes("a.A", "b.B")) + }) +} + func TestEmptyFiles(t *testing.T) { t.Parallel() t.Run("include_empty_file", func(t *testing.T) { diff --git a/private/bufpkg/bufimage/bufimageutil/image_filter.go b/private/bufpkg/bufimage/bufimageutil/image_filter.go index aadd631948..f336c4457f 100644 --- a/private/bufpkg/bufimage/bufimageutil/image_filter.go +++ b/private/bufpkg/bufimage/bufimageutil/image_filter.go @@ -170,12 +170,17 @@ func filterImageFile( return nil, nil // Filtered out. } // A file that contains no included type is kept only because an included - // type references it, so it becomes an import. Files that were already - // imports stay imports. + // type references it, so it becomes an import. A file that was already an + // import stays one, unless WithAllowIncludeOfImportedType is set. isImport := imageFile.IsImport() if len(options.includeTypes) > 0 { if _, ok := closure.includedFiles[imageFile.Path()]; !ok { isImport = true + } else if options.allowImportedTypes { + // When WithAllowIncludeOfImportedType is set, a `--type` type can be + // in a dependency module, in which case we should mark the file containing + // it as a target file. + isImport = false } } if !changed { diff --git a/private/gen/proto/go/buf/alpha/image/v1/image.pb.go b/private/gen/proto/go/buf/alpha/image/v1/image.pb.go index fffcff01f5..03ef77b039 100644 --- a/private/gen/proto/go/buf/alpha/image/v1/image.pb.go +++ b/private/gen/proto/go/buf/alpha/image/v1/image.pb.go @@ -618,6 +618,10 @@ type ImageFileExtension_builder struct { // 3. A file that is not itself a target, but is (possibly transitively) // imported by a target file. // + // When a type filter is allowed to select types from imported files, as the + // BSR does, a file that defines a selected type is a target even if it falls + // under one of the cases above. + // // We use "import" as this matches with the protoc concept of // --include_imports, however import is a bit of an overloaded term. // diff --git a/proto/buf/alpha/image/v1/image.proto b/proto/buf/alpha/image/v1/image.proto index f87212fbe5..d0c742233a 100644 --- a/proto/buf/alpha/image/v1/image.proto +++ b/proto/buf/alpha/image/v1/image.proto @@ -77,6 +77,10 @@ message ImageFileExtension { // 3. A file that is not itself a target, but is (possibly transitively) // imported by a target file. // + // When a type filter is allowed to select types from imported files, as the + // BSR does, a file that defines a selected type is a target even if it falls + // under one of the cases above. + // // We use "import" as this matches with the protoc concept of // --include_imports, however import is a bit of an overloaded term. // From 6fbdda4525cb92647134a9297f8555c29db9a064 Mon Sep 17 00:00:00 2001 From: Oliver Sun Date: Tue, 10 Nov 2026 17:52:31 +0100 Subject: [PATCH 13/16] update image.proto comments --- private/gen/proto/go/buf/alpha/image/v1/image.pb.go | 8 +++----- proto/buf/alpha/image/v1/image.proto | 8 +++----- 2 files changed, 6 insertions(+), 10 deletions(-) diff --git a/private/gen/proto/go/buf/alpha/image/v1/image.pb.go b/private/gen/proto/go/buf/alpha/image/v1/image.pb.go index 03ef77b039..939e6a5287 100644 --- a/private/gen/proto/go/buf/alpha/image/v1/image.pb.go +++ b/private/gen/proto/go/buf/alpha/image/v1/image.pb.go @@ -615,12 +615,10 @@ type ImageFileExtension_builder struct { // // 1. A Well-Known Type included from the compiler. // 2. A file that was included from a Buf module dependency. - // 3. A file that is not itself a target, but is (possibly transitively) - // imported by a target file. + // 3. A file that a target file depends on. // - // When a type filter is allowed to select types from imported files, as the - // BSR does, a file that defines a selected type is a target even if it falls - // under one of the cases above. + // A target file always has its is_import set to false, regardless + // of which Module it derived from. // // We use "import" as this matches with the protoc concept of // --include_imports, however import is a bit of an overloaded term. diff --git a/proto/buf/alpha/image/v1/image.proto b/proto/buf/alpha/image/v1/image.proto index d0c742233a..7a71297f0b 100644 --- a/proto/buf/alpha/image/v1/image.proto +++ b/proto/buf/alpha/image/v1/image.proto @@ -74,12 +74,10 @@ message ImageFileExtension { // // 1. A Well-Known Type included from the compiler. // 2. A file that was included from a Buf module dependency. - // 3. A file that is not itself a target, but is (possibly transitively) - // imported by a target file. + // 3. A file that a target file depends on. // - // When a type filter is allowed to select types from imported files, as the - // BSR does, a file that defines a selected type is a target even if it falls - // under one of the cases above. + // A target file always has its is_import set to false, regardless + // of which Module it derived from. // // We use "import" as this matches with the protoc concept of // --include_imports, however import is a bit of an overloaded term. From 28db5292e719862756102f8c87ad67446928889a Mon Sep 17 00:00:00 2001 From: Oliver Sun Date: Tue, 10 Nov 2026 17:54:33 +0100 Subject: [PATCH 14/16] update changelog --- CHANGELOG.md | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 15874b8d9e..5a139bc2c5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,11 +4,10 @@ - Fix LSP hover and highlighting of CEL expressions whose proto strings contain escape sequences, which placed the ranges of string literals, and of tokens after a `\u` or `\U` escape, wrongly. -- Fix a bug where a file imported by the file of a type specified by `--type`, without defining a - specified type itself, was not considered an import. The `is_import` comment in `image.proto` is - also updated to describe this. Image filtering now has a unified order across commands, where - the path filters apply before the type filters and `--exclude-imports` applies last. Some - consequences: +- Fix `buf build --type=X` filters to correctly mark non-targeted files as imports.The `is_import` + comment in `image.proto` is also updated to describe this. Image filtering now has a unified order + across commands, where the path filters apply during the image build, before the type filters take + place after the image build and `--exclude-imports` applies last. Some consequences: - `buf build --type a.A --exclude-imports` outputs only the file that defines `a.A`, and no longer fails when `a.A` has a field whose type is defined in a dependency or a Well-Known Type. - `buf generate` with `types` no longer generates code for files kept only by reference. To From 5c94ff04bbc57fdad73f4b463ea48864e3c4851f Mon Sep 17 00:00:00 2001 From: Oliver Sun Date: Tue, 10 Nov 2026 18:12:10 +0100 Subject: [PATCH 15/16] update WithIncludeTypes docs --- .../bufpkg/bufimage/bufimageutil/bufimageutil.go | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/private/bufpkg/bufimage/bufimageutil/bufimageutil.go b/private/bufpkg/bufimage/bufimageutil/bufimageutil.go index 41dfa46cba..da3dfe1f62 100644 --- a/private/bufpkg/bufimage/bufimageutil/bufimageutil.go +++ b/private/bufpkg/bufimage/bufimageutil/bufimageutil.go @@ -125,15 +125,13 @@ func WithAllowIncludeOfImportedType() ImageFilterOption { // "acme.foo", all of its sub-packages, and all of their types; "acme.Foo.**" // matches the message "acme.Foo" and all of its nested types. // -// A kept file that contains none of the included types is kept only because -// an included type references a type in it, and is marked as an import. Files -// that are already imports stay imports, unless [WithAllowIncludeOfImportedType] -// is set and the file defines an included type. +// When both `--path` and WithIncludeTypes are passed, without [WithAllowIncludeOfImportedType], +// the resulting image is the union of the paths intersecting the union of the types. In this case, +// the type filter marks a file as an import iff it doesn't contain a filtering type, but does not +// set any file as target files. // -// Normally, if `--path a.proto` is passed it's an error to pass `--type B`, because -// they don't intersect. However, if `--path a.proto` and `--type pkg` is passed and -// both a.proto and b.proto are in the pkg package, then it's valid. This time `--type pkg` -// does not make b.proto a target because it's excluded by `--path a.proto` already. +// With [WithAllowIncludeOfImportedType], `--type` and WithIncludeTypes set, the `--type` filter +// sets a file as a target iff it contains a filtering type, and as an import otherwise. // // If the type does not exist in the image, an error wrapping // [ErrImageFilterTypeNotFound] will be returned. From 31788fef59c5fdcf690613f8892ec17f6f08908b Mon Sep 17 00:00:00 2001 From: Oliver Sun Date: Tue, 10 Nov 2026 18:20:52 +0100 Subject: [PATCH 16/16] fix comments --- CHANGELOG.md | 6 +++--- private/bufpkg/bufimage/bufimageutil/bufimageutil.go | 8 ++++---- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5a139bc2c5..230640a24c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,10 +4,10 @@ - Fix LSP hover and highlighting of CEL expressions whose proto strings contain escape sequences, which placed the ranges of string literals, and of tokens after a `\u` or `\U` escape, wrongly. -- Fix `buf build --type=X` filters to correctly mark non-targeted files as imports.The `is_import` +- Fix `buf build --type=X` filters to correctly mark non-targeted files as imports. The `is_import` comment in `image.proto` is also updated to describe this. Image filtering now has a unified order - across commands, where the path filters apply during the image build, before the type filters take - place after the image build and `--exclude-imports` applies last. Some consequences: + across commands, where `--path` applies first, then `--type`, then `--exclude-imports`. Some + consequences: - `buf build --type a.A --exclude-imports` outputs only the file that defines `a.A`, and no longer fails when `a.A` has a field whose type is defined in a dependency or a Well-Known Type. - `buf generate` with `types` no longer generates code for files kept only by reference. To diff --git a/private/bufpkg/bufimage/bufimageutil/bufimageutil.go b/private/bufpkg/bufimage/bufimageutil/bufimageutil.go index da3dfe1f62..948c2f4b94 100644 --- a/private/bufpkg/bufimage/bufimageutil/bufimageutil.go +++ b/private/bufpkg/bufimage/bufimageutil/bufimageutil.go @@ -127,11 +127,11 @@ func WithAllowIncludeOfImportedType() ImageFilterOption { // // When both `--path` and WithIncludeTypes are passed, without [WithAllowIncludeOfImportedType], // the resulting image is the union of the paths intersecting the union of the types. In this case, -// the type filter marks a file as an import iff it doesn't contain a filtering type, but does not -// set any file as target files. +// the type filter marks a file as an import if it doesn't contain an included type, but does not +// set any file as a target. // -// With [WithAllowIncludeOfImportedType], `--type` and WithIncludeTypes set, the `--type` filter -// sets a file as a target iff it contains a filtering type, and as an import otherwise. +// With [WithAllowIncludeOfImportedType] set, the type filter sets a file as a target iff it +// contains an included type, and as an import otherwise. // // If the type does not exist in the image, an error wrapping // [ErrImageFilterTypeNotFound] will be returned.