cmd/protoc-gen-go: add test for "import option" directive

See https://protobuf.dev/editions/overview/#import-option for context.

Change-Id: Iea73299fdd828301fe451ca3c6b9234e8dd9acf3
Reviewed-on: https://go-review.googlesource.com/c/protobuf/+/699715
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Reviewed-by: Mike Kruskal <mkruskal@google.com>
Reviewed-by: Lasse Folger <lassefolger@google.com>
diff --git a/cmd/protoc-gen-go/importoption_test.go b/cmd/protoc-gen-go/importoption_test.go
new file mode 100644
index 0000000..5492c03
--- /dev/null
+++ b/cmd/protoc-gen-go/importoption_test.go
@@ -0,0 +1,56 @@
+// Copyright 2025 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main
+
+import (
+	"testing"
+
+	"github.com/google/go-cmp/cmp"
+	importoptionpb "google.golang.org/protobuf/cmd/protoc-gen-go/testdata/import_option"
+	"google.golang.org/protobuf/encoding/protowire"
+	"google.golang.org/protobuf/proto"
+	"google.golang.org/protobuf/types/descriptorpb"
+
+	// Ensure the custom option is linked into this test binary.
+	// NB: import_option_unlinked is not linked into this test binary.
+	importoptioncustompb "google.golang.org/protobuf/cmd/protoc-gen-go/testdata/import_option_custom"
+)
+
+func TestImportOption(t *testing.T) {
+	var nilMessage *importoptionpb.TestMessage
+	md := nilMessage.ProtoReflect().Descriptor()
+
+	// Options from import option that are linked in should be available through
+	// the extension API as usual.
+	{
+		fd := md.Fields().ByName("hello")
+		fopts := fd.Options().(*descriptorpb.FieldOptions)
+		if !proto.HasExtension(fopts, importoptioncustompb.E_FieldOption) {
+			t.Errorf("FieldDescriptor(hello) does not have FieldOption extension set")
+		}
+	}
+
+	// Options from import option that are not linked in should be in unknown bytes.
+	{
+		fd := md.Fields().ByName("world")
+		fopts := fd.Options().(*descriptorpb.FieldOptions)
+		unknown := fopts.ProtoReflect().GetUnknown()
+		var fields []protowire.Number
+		b := unknown
+		for len(b) > 0 {
+			num, _, n := protowire.ConsumeField(b)
+			if n < 0 {
+				t.Errorf("FieldDescriptor(world) contains invalid wire format: ConsumeField = %d", n)
+			}
+			fields = append(fields, num)
+			b = b[n:]
+		}
+		want := []protowire.Number{504589222}
+		if diff := cmp.Diff(want, fields); diff != "" {
+			t.Errorf("FieldDescriptor(world) unknown bytes contain unexpected fields: diff (-want +got):\n%s", diff)
+		}
+	}
+
+}
diff --git a/cmd/protoc-gen-go/testdata/gen_test.go b/cmd/protoc-gen-go/testdata/gen_test.go
index 234019b..6eb5905 100644
--- a/cmd/protoc-gen-go/testdata/gen_test.go
+++ b/cmd/protoc-gen-go/testdata/gen_test.go
@@ -15,6 +15,9 @@
 	_ "google.golang.org/protobuf/cmd/protoc-gen-go/testdata/extensions/extra"
 	_ "google.golang.org/protobuf/cmd/protoc-gen-go/testdata/extensions/proto3"
 	_ "google.golang.org/protobuf/cmd/protoc-gen-go/testdata/fieldnames"
+	_ "google.golang.org/protobuf/cmd/protoc-gen-go/testdata/import_option"
+	_ "google.golang.org/protobuf/cmd/protoc-gen-go/testdata/import_option_custom"
+	_ "google.golang.org/protobuf/cmd/protoc-gen-go/testdata/import_option_unlinked"
 	_ "google.golang.org/protobuf/cmd/protoc-gen-go/testdata/import_public"
 	_ "google.golang.org/protobuf/cmd/protoc-gen-go/testdata/import_public/sub"
 	_ "google.golang.org/protobuf/cmd/protoc-gen-go/testdata/import_public/sub2"
diff --git a/cmd/protoc-gen-go/testdata/import_option/import_option.pb.go b/cmd/protoc-gen-go/testdata/import_option/import_option.pb.go
new file mode 100644
index 0000000..428683e
--- /dev/null
+++ b/cmd/protoc-gen-go/testdata/import_option/import_option.pb.go
@@ -0,0 +1,172 @@
+// Copyright 2025 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Code generated by protoc-gen-go. DO NOT EDIT.
+// source: cmd/protoc-gen-go/testdata/import_option/import_option.proto
+
+package import_option
+
+import (
+	protoreflect "google.golang.org/protobuf/reflect/protoreflect"
+	protoimpl "google.golang.org/protobuf/runtime/protoimpl"
+	_ "google.golang.org/protobuf/types/descriptorpb"
+	reflect "reflect"
+	unsafe "unsafe"
+)
+
+type TestMessage struct {
+	state                  protoimpl.MessageState `protogen:"opaque.v1"`
+	xxx_hidden_Hello       *string                `protobuf:"bytes,1,opt,name=hello"`
+	xxx_hidden_World       *string                `protobuf:"bytes,2,opt,name=world"`
+	XXX_raceDetectHookData protoimpl.RaceDetectHookData
+	XXX_presence           [1]uint32
+	unknownFields          protoimpl.UnknownFields
+	sizeCache              protoimpl.SizeCache
+}
+
+func (x *TestMessage) Reset() {
+	*x = TestMessage{}
+	mi := &file_cmd_protoc_gen_go_testdata_import_option_import_option_proto_msgTypes[0]
+	ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+	ms.StoreMessageInfo(mi)
+}
+
+func (x *TestMessage) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*TestMessage) ProtoMessage() {}
+
+func (x *TestMessage) ProtoReflect() protoreflect.Message {
+	mi := &file_cmd_protoc_gen_go_testdata_import_option_import_option_proto_msgTypes[0]
+	if x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+func (x *TestMessage) GetHello() string {
+	if x != nil {
+		if x.xxx_hidden_Hello != nil {
+			return *x.xxx_hidden_Hello
+		}
+		return ""
+	}
+	return ""
+}
+
+func (x *TestMessage) GetWorld() string {
+	if x != nil {
+		if x.xxx_hidden_World != nil {
+			return *x.xxx_hidden_World
+		}
+		return ""
+	}
+	return ""
+}
+
+func (x *TestMessage) SetHello(v string) {
+	x.xxx_hidden_Hello = &v
+	protoimpl.X.SetPresent(&(x.XXX_presence[0]), 0, 2)
+}
+
+func (x *TestMessage) SetWorld(v string) {
+	x.xxx_hidden_World = &v
+	protoimpl.X.SetPresent(&(x.XXX_presence[0]), 1, 2)
+}
+
+func (x *TestMessage) HasHello() bool {
+	if x == nil {
+		return false
+	}
+	return protoimpl.X.Present(&(x.XXX_presence[0]), 0)
+}
+
+func (x *TestMessage) HasWorld() bool {
+	if x == nil {
+		return false
+	}
+	return protoimpl.X.Present(&(x.XXX_presence[0]), 1)
+}
+
+func (x *TestMessage) ClearHello() {
+	protoimpl.X.ClearPresent(&(x.XXX_presence[0]), 0)
+	x.xxx_hidden_Hello = nil
+}
+
+func (x *TestMessage) ClearWorld() {
+	protoimpl.X.ClearPresent(&(x.XXX_presence[0]), 1)
+	x.xxx_hidden_World = nil
+}
+
+type TestMessage_builder struct {
+	_ [0]func() // Prevents comparability and use of unkeyed literals for the builder.
+
+	Hello *string
+	World *string
+}
+
+func (b0 TestMessage_builder) Build() *TestMessage {
+	m0 := &TestMessage{}
+	b, x := &b0, m0
+	_, _ = b, x
+	if b.Hello != nil {
+		protoimpl.X.SetPresentNonAtomic(&(x.XXX_presence[0]), 0, 2)
+		x.xxx_hidden_Hello = b.Hello
+	}
+	if b.World != nil {
+		protoimpl.X.SetPresentNonAtomic(&(x.XXX_presence[0]), 1, 2)
+		x.xxx_hidden_World = b.World
+	}
+	return m0
+}
+
+var File_cmd_protoc_gen_go_testdata_import_option_import_option_proto protoreflect.FileDescriptor
+
+const file_cmd_protoc_gen_go_testdata_import_option_import_option_proto_rawDesc = "" +
+	"\n" +
+	"<cmd/protoc-gen-go/testdata/import_option/import_option.proto\x12\x10testimportoption\x1a google/protobuf/descriptor.proto\"M\n" +
+	"\vTestMessage\x12\x1e\n" +
+	"\x05hello\x18\x01 \x01(\tB\b\xaa\xba\xed\x84\x0f\x02\b\x17R\x05hello\x12\x1e\n" +
+	"\x05world\x18\x02 \x01(\tB\b\xb2\xba\xed\x84\x0f\x02\b\x17R\x05worldBEZCgoogle.golang.org/protobuf/cmd/protoc-gen-go/testdata/import_optionb\beditionsp\xe9\azJcmd/protoc-gen-go/testdata/import_option_custom/import_option_custom.protozNcmd/protoc-gen-go/testdata/import_option_unlinked/import_option_unlinked.proto"
+
+var file_cmd_protoc_gen_go_testdata_import_option_import_option_proto_msgTypes = make([]protoimpl.MessageInfo, 1)
+var file_cmd_protoc_gen_go_testdata_import_option_import_option_proto_goTypes = []any{
+	(*TestMessage)(nil), // 0: testimportoption.TestMessage
+}
+var file_cmd_protoc_gen_go_testdata_import_option_import_option_proto_depIdxs = []int32{
+	0, // [0:0] is the sub-list for method output_type
+	0, // [0:0] is the sub-list for method input_type
+	0, // [0:0] is the sub-list for extension type_name
+	0, // [0:0] is the sub-list for extension extendee
+	0, // [0:0] is the sub-list for field type_name
+}
+
+func init() { file_cmd_protoc_gen_go_testdata_import_option_import_option_proto_init() }
+func file_cmd_protoc_gen_go_testdata_import_option_import_option_proto_init() {
+	if File_cmd_protoc_gen_go_testdata_import_option_import_option_proto != nil {
+		return
+	}
+	type x struct{}
+	out := protoimpl.TypeBuilder{
+		File: protoimpl.DescBuilder{
+			GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
+			RawDescriptor: unsafe.Slice(unsafe.StringData(file_cmd_protoc_gen_go_testdata_import_option_import_option_proto_rawDesc), len(file_cmd_protoc_gen_go_testdata_import_option_import_option_proto_rawDesc)),
+			NumEnums:      0,
+			NumMessages:   1,
+			NumExtensions: 0,
+			NumServices:   0,
+		},
+		GoTypes:           file_cmd_protoc_gen_go_testdata_import_option_import_option_proto_goTypes,
+		DependencyIndexes: file_cmd_protoc_gen_go_testdata_import_option_import_option_proto_depIdxs,
+		MessageInfos:      file_cmd_protoc_gen_go_testdata_import_option_import_option_proto_msgTypes,
+	}.Build()
+	File_cmd_protoc_gen_go_testdata_import_option_import_option_proto = out.File
+	file_cmd_protoc_gen_go_testdata_import_option_import_option_proto_goTypes = nil
+	file_cmd_protoc_gen_go_testdata_import_option_import_option_proto_depIdxs = nil
+}
diff --git a/cmd/protoc-gen-go/testdata/import_option/import_option.proto b/cmd/protoc-gen-go/testdata/import_option/import_option.proto
new file mode 100644
index 0000000..d5d25bc
--- /dev/null
+++ b/cmd/protoc-gen-go/testdata/import_option/import_option.proto
@@ -0,0 +1,18 @@
+// Copyright 2025 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+edition = "2024";
+
+package testimportoption;
+
+import "google/protobuf/descriptor.proto";
+import option "cmd/protoc-gen-go/testdata/import_option_custom/import_option_custom.proto";
+import option "cmd/protoc-gen-go/testdata/import_option_unlinked/import_option_unlinked.proto";
+
+option go_package = "google.golang.org/protobuf/cmd/protoc-gen-go/testdata/import_option";
+
+message TestMessage {
+  string hello = 1 [(testimportoption_custom.field_option) = { plain_field: 23 }];
+  string world = 2 [(testimportoption_unlinked.field_option) = { plain_field: 23 }];
+}
diff --git a/cmd/protoc-gen-go/testdata/import_option_custom/import_option_custom.pb.go b/cmd/protoc-gen-go/testdata/import_option_custom/import_option_custom.pb.go
new file mode 100644
index 0000000..b5feec8
--- /dev/null
+++ b/cmd/protoc-gen-go/testdata/import_option_custom/import_option_custom.pb.go
@@ -0,0 +1,158 @@
+// Copyright 2025 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Code generated by protoc-gen-go. DO NOT EDIT.
+// source: cmd/protoc-gen-go/testdata/import_option_custom/import_option_custom.proto
+
+package import_option_custom
+
+import (
+	protoreflect "google.golang.org/protobuf/reflect/protoreflect"
+	protoimpl "google.golang.org/protobuf/runtime/protoimpl"
+	descriptorpb "google.golang.org/protobuf/types/descriptorpb"
+	reflect "reflect"
+	unsafe "unsafe"
+)
+
+type OptionsMessage struct {
+	state                  protoimpl.MessageState `protogen:"opaque.v1"`
+	xxx_hidden_PlainField  int32                  `protobuf:"varint,1,opt,name=plain_field,json=plainField"`
+	XXX_raceDetectHookData protoimpl.RaceDetectHookData
+	XXX_presence           [1]uint32
+	unknownFields          protoimpl.UnknownFields
+	sizeCache              protoimpl.SizeCache
+}
+
+func (x *OptionsMessage) Reset() {
+	*x = OptionsMessage{}
+	mi := &file_cmd_protoc_gen_go_testdata_import_option_custom_import_option_custom_proto_msgTypes[0]
+	ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+	ms.StoreMessageInfo(mi)
+}
+
+func (x *OptionsMessage) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*OptionsMessage) ProtoMessage() {}
+
+func (x *OptionsMessage) ProtoReflect() protoreflect.Message {
+	mi := &file_cmd_protoc_gen_go_testdata_import_option_custom_import_option_custom_proto_msgTypes[0]
+	if x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+func (x *OptionsMessage) GetPlainField() int32 {
+	if x != nil {
+		return x.xxx_hidden_PlainField
+	}
+	return 0
+}
+
+func (x *OptionsMessage) SetPlainField(v int32) {
+	x.xxx_hidden_PlainField = v
+	protoimpl.X.SetPresent(&(x.XXX_presence[0]), 0, 1)
+}
+
+func (x *OptionsMessage) HasPlainField() bool {
+	if x == nil {
+		return false
+	}
+	return protoimpl.X.Present(&(x.XXX_presence[0]), 0)
+}
+
+func (x *OptionsMessage) ClearPlainField() {
+	protoimpl.X.ClearPresent(&(x.XXX_presence[0]), 0)
+	x.xxx_hidden_PlainField = 0
+}
+
+type OptionsMessage_builder struct {
+	_ [0]func() // Prevents comparability and use of unkeyed literals for the builder.
+
+	PlainField *int32
+}
+
+func (b0 OptionsMessage_builder) Build() *OptionsMessage {
+	m0 := &OptionsMessage{}
+	b, x := &b0, m0
+	_, _ = b, x
+	if b.PlainField != nil {
+		protoimpl.X.SetPresentNonAtomic(&(x.XXX_presence[0]), 0, 1)
+		x.xxx_hidden_PlainField = *b.PlainField
+	}
+	return m0
+}
+
+var file_cmd_protoc_gen_go_testdata_import_option_custom_import_option_custom_proto_extTypes = []protoimpl.ExtensionInfo{
+	{
+		ExtendedType:  (*descriptorpb.FieldOptions)(nil),
+		ExtensionType: (*OptionsMessage)(nil),
+		Field:         504589221,
+		Name:          "testimportoption_custom.field_option",
+		Tag:           "bytes,504589221,opt,name=field_option",
+		Filename:      "cmd/protoc-gen-go/testdata/import_option_custom/import_option_custom.proto",
+	},
+}
+
+// Extension fields to descriptorpb.FieldOptions.
+var (
+	// optional testimportoption_custom.OptionsMessage field_option = 504589221;
+	E_FieldOption = &file_cmd_protoc_gen_go_testdata_import_option_custom_import_option_custom_proto_extTypes[0]
+)
+
+var File_cmd_protoc_gen_go_testdata_import_option_custom_import_option_custom_proto protoreflect.FileDescriptor
+
+const file_cmd_protoc_gen_go_testdata_import_option_custom_import_option_custom_proto_rawDesc = "" +
+	"\n" +
+	"Jcmd/protoc-gen-go/testdata/import_option_custom/import_option_custom.proto\x12\x17testimportoption_custom\x1a google/protobuf/descriptor.proto\"1\n" +
+	"\x0eOptionsMessage\x12\x1f\n" +
+	"\vplain_field\x18\x01 \x01(\x05R\n" +
+	"plainField:m\n" +
+	"\ffield_option\x12\x1d.google.protobuf.FieldOptions\x18\xa5\xd7\xcd\xf0\x01 \x01(\v2'.testimportoption_custom.OptionsMessageR\vfieldOptionBLZJgoogle.golang.org/protobuf/cmd/protoc-gen-go/testdata/import_option_customb\beditionsp\xe9\a"
+
+var file_cmd_protoc_gen_go_testdata_import_option_custom_import_option_custom_proto_msgTypes = make([]protoimpl.MessageInfo, 1)
+var file_cmd_protoc_gen_go_testdata_import_option_custom_import_option_custom_proto_goTypes = []any{
+	(*OptionsMessage)(nil),            // 0: testimportoption_custom.OptionsMessage
+	(*descriptorpb.FieldOptions)(nil), // 1: google.protobuf.FieldOptions
+}
+var file_cmd_protoc_gen_go_testdata_import_option_custom_import_option_custom_proto_depIdxs = []int32{
+	1, // 0: testimportoption_custom.field_option:extendee -> google.protobuf.FieldOptions
+	0, // 1: testimportoption_custom.field_option:type_name -> testimportoption_custom.OptionsMessage
+	2, // [2:2] is the sub-list for method output_type
+	2, // [2:2] is the sub-list for method input_type
+	1, // [1:2] is the sub-list for extension type_name
+	0, // [0:1] is the sub-list for extension extendee
+	0, // [0:0] is the sub-list for field type_name
+}
+
+func init() { file_cmd_protoc_gen_go_testdata_import_option_custom_import_option_custom_proto_init() }
+func file_cmd_protoc_gen_go_testdata_import_option_custom_import_option_custom_proto_init() {
+	if File_cmd_protoc_gen_go_testdata_import_option_custom_import_option_custom_proto != nil {
+		return
+	}
+	type x struct{}
+	out := protoimpl.TypeBuilder{
+		File: protoimpl.DescBuilder{
+			GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
+			RawDescriptor: unsafe.Slice(unsafe.StringData(file_cmd_protoc_gen_go_testdata_import_option_custom_import_option_custom_proto_rawDesc), len(file_cmd_protoc_gen_go_testdata_import_option_custom_import_option_custom_proto_rawDesc)),
+			NumEnums:      0,
+			NumMessages:   1,
+			NumExtensions: 1,
+			NumServices:   0,
+		},
+		GoTypes:           file_cmd_protoc_gen_go_testdata_import_option_custom_import_option_custom_proto_goTypes,
+		DependencyIndexes: file_cmd_protoc_gen_go_testdata_import_option_custom_import_option_custom_proto_depIdxs,
+		MessageInfos:      file_cmd_protoc_gen_go_testdata_import_option_custom_import_option_custom_proto_msgTypes,
+		ExtensionInfos:    file_cmd_protoc_gen_go_testdata_import_option_custom_import_option_custom_proto_extTypes,
+	}.Build()
+	File_cmd_protoc_gen_go_testdata_import_option_custom_import_option_custom_proto = out.File
+	file_cmd_protoc_gen_go_testdata_import_option_custom_import_option_custom_proto_goTypes = nil
+	file_cmd_protoc_gen_go_testdata_import_option_custom_import_option_custom_proto_depIdxs = nil
+}
diff --git a/cmd/protoc-gen-go/testdata/import_option_custom/import_option_custom.proto b/cmd/protoc-gen-go/testdata/import_option_custom/import_option_custom.proto
new file mode 100644
index 0000000..6befc41
--- /dev/null
+++ b/cmd/protoc-gen-go/testdata/import_option_custom/import_option_custom.proto
@@ -0,0 +1,19 @@
+// Copyright 2025 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+edition = "2024";
+
+package testimportoption_custom;
+
+import "google/protobuf/descriptor.proto";
+
+option go_package = "google.golang.org/protobuf/cmd/protoc-gen-go/testdata/import_option_custom";
+
+extend google.protobuf.FieldOptions {
+  OptionsMessage field_option = 504589221;
+}
+
+message OptionsMessage {
+  int32 plain_field = 1;
+}
diff --git a/cmd/protoc-gen-go/testdata/import_option_unlinked/import_option_unlinked.pb.go b/cmd/protoc-gen-go/testdata/import_option_unlinked/import_option_unlinked.pb.go
new file mode 100644
index 0000000..8d6ee67
--- /dev/null
+++ b/cmd/protoc-gen-go/testdata/import_option_unlinked/import_option_unlinked.pb.go
@@ -0,0 +1,160 @@
+// Copyright 2025 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Code generated by protoc-gen-go. DO NOT EDIT.
+// source: cmd/protoc-gen-go/testdata/import_option_unlinked/import_option_unlinked.proto
+
+package import_option_unlinked
+
+import (
+	protoreflect "google.golang.org/protobuf/reflect/protoreflect"
+	protoimpl "google.golang.org/protobuf/runtime/protoimpl"
+	descriptorpb "google.golang.org/protobuf/types/descriptorpb"
+	reflect "reflect"
+	unsafe "unsafe"
+)
+
+type OptionsMessage struct {
+	state                  protoimpl.MessageState `protogen:"opaque.v1"`
+	xxx_hidden_PlainField  int32                  `protobuf:"varint,1,opt,name=plain_field,json=plainField"`
+	XXX_raceDetectHookData protoimpl.RaceDetectHookData
+	XXX_presence           [1]uint32
+	unknownFields          protoimpl.UnknownFields
+	sizeCache              protoimpl.SizeCache
+}
+
+func (x *OptionsMessage) Reset() {
+	*x = OptionsMessage{}
+	mi := &file_cmd_protoc_gen_go_testdata_import_option_unlinked_import_option_unlinked_proto_msgTypes[0]
+	ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+	ms.StoreMessageInfo(mi)
+}
+
+func (x *OptionsMessage) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*OptionsMessage) ProtoMessage() {}
+
+func (x *OptionsMessage) ProtoReflect() protoreflect.Message {
+	mi := &file_cmd_protoc_gen_go_testdata_import_option_unlinked_import_option_unlinked_proto_msgTypes[0]
+	if x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+func (x *OptionsMessage) GetPlainField() int32 {
+	if x != nil {
+		return x.xxx_hidden_PlainField
+	}
+	return 0
+}
+
+func (x *OptionsMessage) SetPlainField(v int32) {
+	x.xxx_hidden_PlainField = v
+	protoimpl.X.SetPresent(&(x.XXX_presence[0]), 0, 1)
+}
+
+func (x *OptionsMessage) HasPlainField() bool {
+	if x == nil {
+		return false
+	}
+	return protoimpl.X.Present(&(x.XXX_presence[0]), 0)
+}
+
+func (x *OptionsMessage) ClearPlainField() {
+	protoimpl.X.ClearPresent(&(x.XXX_presence[0]), 0)
+	x.xxx_hidden_PlainField = 0
+}
+
+type OptionsMessage_builder struct {
+	_ [0]func() // Prevents comparability and use of unkeyed literals for the builder.
+
+	PlainField *int32
+}
+
+func (b0 OptionsMessage_builder) Build() *OptionsMessage {
+	m0 := &OptionsMessage{}
+	b, x := &b0, m0
+	_, _ = b, x
+	if b.PlainField != nil {
+		protoimpl.X.SetPresentNonAtomic(&(x.XXX_presence[0]), 0, 1)
+		x.xxx_hidden_PlainField = *b.PlainField
+	}
+	return m0
+}
+
+var file_cmd_protoc_gen_go_testdata_import_option_unlinked_import_option_unlinked_proto_extTypes = []protoimpl.ExtensionInfo{
+	{
+		ExtendedType:  (*descriptorpb.FieldOptions)(nil),
+		ExtensionType: (*OptionsMessage)(nil),
+		Field:         504589222,
+		Name:          "testimportoption_unlinked.field_option",
+		Tag:           "bytes,504589222,opt,name=field_option",
+		Filename:      "cmd/protoc-gen-go/testdata/import_option_unlinked/import_option_unlinked.proto",
+	},
+}
+
+// Extension fields to descriptorpb.FieldOptions.
+var (
+	// optional testimportoption_unlinked.OptionsMessage field_option = 504589222;
+	E_FieldOption = &file_cmd_protoc_gen_go_testdata_import_option_unlinked_import_option_unlinked_proto_extTypes[0]
+)
+
+var File_cmd_protoc_gen_go_testdata_import_option_unlinked_import_option_unlinked_proto protoreflect.FileDescriptor
+
+const file_cmd_protoc_gen_go_testdata_import_option_unlinked_import_option_unlinked_proto_rawDesc = "" +
+	"\n" +
+	"Ncmd/protoc-gen-go/testdata/import_option_unlinked/import_option_unlinked.proto\x12\x19testimportoption_unlinked\x1a google/protobuf/descriptor.proto\"1\n" +
+	"\x0eOptionsMessage\x12\x1f\n" +
+	"\vplain_field\x18\x01 \x01(\x05R\n" +
+	"plainField:o\n" +
+	"\ffield_option\x12\x1d.google.protobuf.FieldOptions\x18\xa6\xd7\xcd\xf0\x01 \x01(\v2).testimportoption_unlinked.OptionsMessageR\vfieldOptionBNZLgoogle.golang.org/protobuf/cmd/protoc-gen-go/testdata/import_option_unlinkedb\beditionsp\xe9\a"
+
+var file_cmd_protoc_gen_go_testdata_import_option_unlinked_import_option_unlinked_proto_msgTypes = make([]protoimpl.MessageInfo, 1)
+var file_cmd_protoc_gen_go_testdata_import_option_unlinked_import_option_unlinked_proto_goTypes = []any{
+	(*OptionsMessage)(nil),            // 0: testimportoption_unlinked.OptionsMessage
+	(*descriptorpb.FieldOptions)(nil), // 1: google.protobuf.FieldOptions
+}
+var file_cmd_protoc_gen_go_testdata_import_option_unlinked_import_option_unlinked_proto_depIdxs = []int32{
+	1, // 0: testimportoption_unlinked.field_option:extendee -> google.protobuf.FieldOptions
+	0, // 1: testimportoption_unlinked.field_option:type_name -> testimportoption_unlinked.OptionsMessage
+	2, // [2:2] is the sub-list for method output_type
+	2, // [2:2] is the sub-list for method input_type
+	1, // [1:2] is the sub-list for extension type_name
+	0, // [0:1] is the sub-list for extension extendee
+	0, // [0:0] is the sub-list for field type_name
+}
+
+func init() {
+	file_cmd_protoc_gen_go_testdata_import_option_unlinked_import_option_unlinked_proto_init()
+}
+func file_cmd_protoc_gen_go_testdata_import_option_unlinked_import_option_unlinked_proto_init() {
+	if File_cmd_protoc_gen_go_testdata_import_option_unlinked_import_option_unlinked_proto != nil {
+		return
+	}
+	type x struct{}
+	out := protoimpl.TypeBuilder{
+		File: protoimpl.DescBuilder{
+			GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
+			RawDescriptor: unsafe.Slice(unsafe.StringData(file_cmd_protoc_gen_go_testdata_import_option_unlinked_import_option_unlinked_proto_rawDesc), len(file_cmd_protoc_gen_go_testdata_import_option_unlinked_import_option_unlinked_proto_rawDesc)),
+			NumEnums:      0,
+			NumMessages:   1,
+			NumExtensions: 1,
+			NumServices:   0,
+		},
+		GoTypes:           file_cmd_protoc_gen_go_testdata_import_option_unlinked_import_option_unlinked_proto_goTypes,
+		DependencyIndexes: file_cmd_protoc_gen_go_testdata_import_option_unlinked_import_option_unlinked_proto_depIdxs,
+		MessageInfos:      file_cmd_protoc_gen_go_testdata_import_option_unlinked_import_option_unlinked_proto_msgTypes,
+		ExtensionInfos:    file_cmd_protoc_gen_go_testdata_import_option_unlinked_import_option_unlinked_proto_extTypes,
+	}.Build()
+	File_cmd_protoc_gen_go_testdata_import_option_unlinked_import_option_unlinked_proto = out.File
+	file_cmd_protoc_gen_go_testdata_import_option_unlinked_import_option_unlinked_proto_goTypes = nil
+	file_cmd_protoc_gen_go_testdata_import_option_unlinked_import_option_unlinked_proto_depIdxs = nil
+}
diff --git a/cmd/protoc-gen-go/testdata/import_option_unlinked/import_option_unlinked.proto b/cmd/protoc-gen-go/testdata/import_option_unlinked/import_option_unlinked.proto
new file mode 100644
index 0000000..8f85f3d
--- /dev/null
+++ b/cmd/protoc-gen-go/testdata/import_option_unlinked/import_option_unlinked.proto
@@ -0,0 +1,19 @@
+// Copyright 2025 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+edition = "2024";
+
+package testimportoption_unlinked;
+
+import "google/protobuf/descriptor.proto";
+
+option go_package = "google.golang.org/protobuf/cmd/protoc-gen-go/testdata/import_option_unlinked";
+
+extend google.protobuf.FieldOptions {
+  OptionsMessage field_option = 504589222;
+}
+
+message OptionsMessage {
+  int32 plain_field = 1;
+}