reflect/protodesc: edition-2024-specific properties should not be lost when converting FileDescriptorProto to protoreflect.FileDescriptor

Updates the protodesc package and the internal filedesc representation so that new language
features in edition 2024 (option imports, message and enum visibility) are not lost when
converting a FileDescriptorProto to a protoreflect.FileDescriptor and vice versa.

Resolves golang/protobuf#1698

Change-Id: I1bf0bd1ef7da24037a1f1438d1d4d54ee078ce72
Reviewed-on: https://go-review.googlesource.com/c/protobuf/+/704415
Reviewed-by: Michael Stapelberg <stapelberg@google.com>
Reviewed-by: Lasse Folger <lassefolger@google.com>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
diff --git a/cmd/protoc-gen-go/importoption_test.go b/cmd/protoc-gen-go/importoption_test.go
index 5492c03..a1d69a1 100644
--- a/cmd/protoc-gen-go/importoption_test.go
+++ b/cmd/protoc-gen-go/importoption_test.go
@@ -5,12 +5,14 @@
 package main
 
 import (
+	"reflect"
 	"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/reflect/protodesc"
 	"google.golang.org/protobuf/types/descriptorpb"
 
 	// Ensure the custom option is linked into this test binary.
@@ -53,4 +55,17 @@
 		}
 	}
 
+	// Verify that the option imports aren't lost when converting to descriptor protos.
+	{
+		fd := md.ParentFile()
+		fdProto := protodesc.ToFileDescriptorProto(fd)
+		expectedOptionImports := []string{
+			"cmd/protoc-gen-go/testdata/import_option_custom/import_option_custom.proto",
+			"cmd/protoc-gen-go/testdata/import_option_unlinked/import_option_unlinked.proto",
+		}
+		if !reflect.DeepEqual(expectedOptionImports, fdProto.GetOptionDependency()) {
+			t.Errorf("option_dependency not correctly populated from protoreflect.FileDescriptor: want %v; got %v",
+				expectedOptionImports, fdProto.GetOptionDependency())
+		}
+	}
 }
diff --git a/cmd/protoc-gen-go/testdata/gen_test.go b/cmd/protoc-gen-go/testdata/gen_test.go
index 6eb5905..4c7e930 100644
--- a/cmd/protoc-gen-go/testdata/gen_test.go
+++ b/cmd/protoc-gen-go/testdata/gen_test.go
@@ -33,4 +33,5 @@
 	_ "google.golang.org/protobuf/cmd/protoc-gen-go/testdata/proto3"
 	_ "google.golang.org/protobuf/cmd/protoc-gen-go/testdata/protoeditions"
 	_ "google.golang.org/protobuf/cmd/protoc-gen-go/testdata/retention"
+	_ "google.golang.org/protobuf/cmd/protoc-gen-go/testdata/visibility"
 )
diff --git a/cmd/protoc-gen-go/testdata/visibility/visibility.pb.go b/cmd/protoc-gen-go/testdata/visibility/visibility.pb.go
new file mode 100644
index 0000000..88cae91
--- /dev/null
+++ b/cmd/protoc-gen-go/testdata/visibility/visibility.pb.go
@@ -0,0 +1,1410 @@
+// Copyright 2020 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/visibility/visibility.proto
+
+package visibility
+
+import (
+	protoreflect "google.golang.org/protobuf/reflect/protoreflect"
+	protoimpl "google.golang.org/protobuf/runtime/protoimpl"
+	reflect "reflect"
+	unsafe "unsafe"
+)
+
+type DefaultEnum int32
+
+const (
+	DefaultEnum_DEFAULT_ZERO DefaultEnum = 0
+)
+
+// Enum value maps for DefaultEnum.
+var (
+	DefaultEnum_name = map[int32]string{
+		0: "DEFAULT_ZERO",
+	}
+	DefaultEnum_value = map[string]int32{
+		"DEFAULT_ZERO": 0,
+	}
+)
+
+func (x DefaultEnum) Enum() *DefaultEnum {
+	p := new(DefaultEnum)
+	*p = x
+	return p
+}
+
+func (x DefaultEnum) String() string {
+	return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x))
+}
+
+func (DefaultEnum) Descriptor() protoreflect.EnumDescriptor {
+	return file_cmd_protoc_gen_go_testdata_visibility_visibility_proto_enumTypes[0].Descriptor()
+}
+
+func (DefaultEnum) Type() protoreflect.EnumType {
+	return &file_cmd_protoc_gen_go_testdata_visibility_visibility_proto_enumTypes[0]
+}
+
+func (x DefaultEnum) Number() protoreflect.EnumNumber {
+	return protoreflect.EnumNumber(x)
+}
+
+type ExportEnum int32
+
+const (
+	ExportEnum_EXPORT_ZERO ExportEnum = 0
+)
+
+// Enum value maps for ExportEnum.
+var (
+	ExportEnum_name = map[int32]string{
+		0: "EXPORT_ZERO",
+	}
+	ExportEnum_value = map[string]int32{
+		"EXPORT_ZERO": 0,
+	}
+)
+
+func (x ExportEnum) Enum() *ExportEnum {
+	p := new(ExportEnum)
+	*p = x
+	return p
+}
+
+func (x ExportEnum) String() string {
+	return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x))
+}
+
+func (ExportEnum) Descriptor() protoreflect.EnumDescriptor {
+	return file_cmd_protoc_gen_go_testdata_visibility_visibility_proto_enumTypes[1].Descriptor()
+}
+
+func (ExportEnum) Type() protoreflect.EnumType {
+	return &file_cmd_protoc_gen_go_testdata_visibility_visibility_proto_enumTypes[1]
+}
+
+func (x ExportEnum) Number() protoreflect.EnumNumber {
+	return protoreflect.EnumNumber(x)
+}
+
+type LocalEnum int32
+
+const (
+	LocalEnum_LOCAL_ZERO LocalEnum = 0
+)
+
+// Enum value maps for LocalEnum.
+var (
+	LocalEnum_name = map[int32]string{
+		0: "LOCAL_ZERO",
+	}
+	LocalEnum_value = map[string]int32{
+		"LOCAL_ZERO": 0,
+	}
+)
+
+func (x LocalEnum) Enum() *LocalEnum {
+	p := new(LocalEnum)
+	*p = x
+	return p
+}
+
+func (x LocalEnum) String() string {
+	return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x))
+}
+
+func (LocalEnum) Descriptor() protoreflect.EnumDescriptor {
+	return file_cmd_protoc_gen_go_testdata_visibility_visibility_proto_enumTypes[2].Descriptor()
+}
+
+func (LocalEnum) Type() protoreflect.EnumType {
+	return &file_cmd_protoc_gen_go_testdata_visibility_visibility_proto_enumTypes[2]
+}
+
+func (x LocalEnum) Number() protoreflect.EnumNumber {
+	return protoreflect.EnumNumber(x)
+}
+
+type DefaultMessage_NestedDefaultEnum int32
+
+const (
+	DefaultMessage_DEFAULT_ZERO DefaultMessage_NestedDefaultEnum = 0
+)
+
+// Enum value maps for DefaultMessage_NestedDefaultEnum.
+var (
+	DefaultMessage_NestedDefaultEnum_name = map[int32]string{
+		0: "DEFAULT_ZERO",
+	}
+	DefaultMessage_NestedDefaultEnum_value = map[string]int32{
+		"DEFAULT_ZERO": 0,
+	}
+)
+
+func (x DefaultMessage_NestedDefaultEnum) Enum() *DefaultMessage_NestedDefaultEnum {
+	p := new(DefaultMessage_NestedDefaultEnum)
+	*p = x
+	return p
+}
+
+func (x DefaultMessage_NestedDefaultEnum) String() string {
+	return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x))
+}
+
+func (DefaultMessage_NestedDefaultEnum) Descriptor() protoreflect.EnumDescriptor {
+	return file_cmd_protoc_gen_go_testdata_visibility_visibility_proto_enumTypes[3].Descriptor()
+}
+
+func (DefaultMessage_NestedDefaultEnum) Type() protoreflect.EnumType {
+	return &file_cmd_protoc_gen_go_testdata_visibility_visibility_proto_enumTypes[3]
+}
+
+func (x DefaultMessage_NestedDefaultEnum) Number() protoreflect.EnumNumber {
+	return protoreflect.EnumNumber(x)
+}
+
+type DefaultMessage_NestedExportEnum int32
+
+const (
+	DefaultMessage_EXPORT_ZERO DefaultMessage_NestedExportEnum = 0
+)
+
+// Enum value maps for DefaultMessage_NestedExportEnum.
+var (
+	DefaultMessage_NestedExportEnum_name = map[int32]string{
+		0: "EXPORT_ZERO",
+	}
+	DefaultMessage_NestedExportEnum_value = map[string]int32{
+		"EXPORT_ZERO": 0,
+	}
+)
+
+func (x DefaultMessage_NestedExportEnum) Enum() *DefaultMessage_NestedExportEnum {
+	p := new(DefaultMessage_NestedExportEnum)
+	*p = x
+	return p
+}
+
+func (x DefaultMessage_NestedExportEnum) String() string {
+	return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x))
+}
+
+func (DefaultMessage_NestedExportEnum) Descriptor() protoreflect.EnumDescriptor {
+	return file_cmd_protoc_gen_go_testdata_visibility_visibility_proto_enumTypes[4].Descriptor()
+}
+
+func (DefaultMessage_NestedExportEnum) Type() protoreflect.EnumType {
+	return &file_cmd_protoc_gen_go_testdata_visibility_visibility_proto_enumTypes[4]
+}
+
+func (x DefaultMessage_NestedExportEnum) Number() protoreflect.EnumNumber {
+	return protoreflect.EnumNumber(x)
+}
+
+type DefaultMessage_NestedLocalEnum int32
+
+const (
+	DefaultMessage_LOCAL_ZERO DefaultMessage_NestedLocalEnum = 0
+)
+
+// Enum value maps for DefaultMessage_NestedLocalEnum.
+var (
+	DefaultMessage_NestedLocalEnum_name = map[int32]string{
+		0: "LOCAL_ZERO",
+	}
+	DefaultMessage_NestedLocalEnum_value = map[string]int32{
+		"LOCAL_ZERO": 0,
+	}
+)
+
+func (x DefaultMessage_NestedLocalEnum) Enum() *DefaultMessage_NestedLocalEnum {
+	p := new(DefaultMessage_NestedLocalEnum)
+	*p = x
+	return p
+}
+
+func (x DefaultMessage_NestedLocalEnum) String() string {
+	return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x))
+}
+
+func (DefaultMessage_NestedLocalEnum) Descriptor() protoreflect.EnumDescriptor {
+	return file_cmd_protoc_gen_go_testdata_visibility_visibility_proto_enumTypes[5].Descriptor()
+}
+
+func (DefaultMessage_NestedLocalEnum) Type() protoreflect.EnumType {
+	return &file_cmd_protoc_gen_go_testdata_visibility_visibility_proto_enumTypes[5]
+}
+
+func (x DefaultMessage_NestedLocalEnum) Number() protoreflect.EnumNumber {
+	return protoreflect.EnumNumber(x)
+}
+
+type ExportMessage_NestedDefaultEnum int32
+
+const (
+	ExportMessage_DEFAULT_ZERO ExportMessage_NestedDefaultEnum = 0
+)
+
+// Enum value maps for ExportMessage_NestedDefaultEnum.
+var (
+	ExportMessage_NestedDefaultEnum_name = map[int32]string{
+		0: "DEFAULT_ZERO",
+	}
+	ExportMessage_NestedDefaultEnum_value = map[string]int32{
+		"DEFAULT_ZERO": 0,
+	}
+)
+
+func (x ExportMessage_NestedDefaultEnum) Enum() *ExportMessage_NestedDefaultEnum {
+	p := new(ExportMessage_NestedDefaultEnum)
+	*p = x
+	return p
+}
+
+func (x ExportMessage_NestedDefaultEnum) String() string {
+	return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x))
+}
+
+func (ExportMessage_NestedDefaultEnum) Descriptor() protoreflect.EnumDescriptor {
+	return file_cmd_protoc_gen_go_testdata_visibility_visibility_proto_enumTypes[6].Descriptor()
+}
+
+func (ExportMessage_NestedDefaultEnum) Type() protoreflect.EnumType {
+	return &file_cmd_protoc_gen_go_testdata_visibility_visibility_proto_enumTypes[6]
+}
+
+func (x ExportMessage_NestedDefaultEnum) Number() protoreflect.EnumNumber {
+	return protoreflect.EnumNumber(x)
+}
+
+type ExportMessage_NestedExportEnum int32
+
+const (
+	ExportMessage_EXPORT_ZERO ExportMessage_NestedExportEnum = 0
+)
+
+// Enum value maps for ExportMessage_NestedExportEnum.
+var (
+	ExportMessage_NestedExportEnum_name = map[int32]string{
+		0: "EXPORT_ZERO",
+	}
+	ExportMessage_NestedExportEnum_value = map[string]int32{
+		"EXPORT_ZERO": 0,
+	}
+)
+
+func (x ExportMessage_NestedExportEnum) Enum() *ExportMessage_NestedExportEnum {
+	p := new(ExportMessage_NestedExportEnum)
+	*p = x
+	return p
+}
+
+func (x ExportMessage_NestedExportEnum) String() string {
+	return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x))
+}
+
+func (ExportMessage_NestedExportEnum) Descriptor() protoreflect.EnumDescriptor {
+	return file_cmd_protoc_gen_go_testdata_visibility_visibility_proto_enumTypes[7].Descriptor()
+}
+
+func (ExportMessage_NestedExportEnum) Type() protoreflect.EnumType {
+	return &file_cmd_protoc_gen_go_testdata_visibility_visibility_proto_enumTypes[7]
+}
+
+func (x ExportMessage_NestedExportEnum) Number() protoreflect.EnumNumber {
+	return protoreflect.EnumNumber(x)
+}
+
+type ExportMessage_NestedLocalEnum int32
+
+const (
+	ExportMessage_LOCAL_ZERO ExportMessage_NestedLocalEnum = 0
+)
+
+// Enum value maps for ExportMessage_NestedLocalEnum.
+var (
+	ExportMessage_NestedLocalEnum_name = map[int32]string{
+		0: "LOCAL_ZERO",
+	}
+	ExportMessage_NestedLocalEnum_value = map[string]int32{
+		"LOCAL_ZERO": 0,
+	}
+)
+
+func (x ExportMessage_NestedLocalEnum) Enum() *ExportMessage_NestedLocalEnum {
+	p := new(ExportMessage_NestedLocalEnum)
+	*p = x
+	return p
+}
+
+func (x ExportMessage_NestedLocalEnum) String() string {
+	return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x))
+}
+
+func (ExportMessage_NestedLocalEnum) Descriptor() protoreflect.EnumDescriptor {
+	return file_cmd_protoc_gen_go_testdata_visibility_visibility_proto_enumTypes[8].Descriptor()
+}
+
+func (ExportMessage_NestedLocalEnum) Type() protoreflect.EnumType {
+	return &file_cmd_protoc_gen_go_testdata_visibility_visibility_proto_enumTypes[8]
+}
+
+func (x ExportMessage_NestedLocalEnum) Number() protoreflect.EnumNumber {
+	return protoreflect.EnumNumber(x)
+}
+
+type LocalMessage_NestedDefaultEnum int32
+
+const (
+	LocalMessage_DEFAULT_ZERO LocalMessage_NestedDefaultEnum = 0
+)
+
+// Enum value maps for LocalMessage_NestedDefaultEnum.
+var (
+	LocalMessage_NestedDefaultEnum_name = map[int32]string{
+		0: "DEFAULT_ZERO",
+	}
+	LocalMessage_NestedDefaultEnum_value = map[string]int32{
+		"DEFAULT_ZERO": 0,
+	}
+)
+
+func (x LocalMessage_NestedDefaultEnum) Enum() *LocalMessage_NestedDefaultEnum {
+	p := new(LocalMessage_NestedDefaultEnum)
+	*p = x
+	return p
+}
+
+func (x LocalMessage_NestedDefaultEnum) String() string {
+	return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x))
+}
+
+func (LocalMessage_NestedDefaultEnum) Descriptor() protoreflect.EnumDescriptor {
+	return file_cmd_protoc_gen_go_testdata_visibility_visibility_proto_enumTypes[9].Descriptor()
+}
+
+func (LocalMessage_NestedDefaultEnum) Type() protoreflect.EnumType {
+	return &file_cmd_protoc_gen_go_testdata_visibility_visibility_proto_enumTypes[9]
+}
+
+func (x LocalMessage_NestedDefaultEnum) Number() protoreflect.EnumNumber {
+	return protoreflect.EnumNumber(x)
+}
+
+type LocalMessage_NestedExportEnum int32
+
+const (
+	LocalMessage_EXPORT_ZERO LocalMessage_NestedExportEnum = 0
+)
+
+// Enum value maps for LocalMessage_NestedExportEnum.
+var (
+	LocalMessage_NestedExportEnum_name = map[int32]string{
+		0: "EXPORT_ZERO",
+	}
+	LocalMessage_NestedExportEnum_value = map[string]int32{
+		"EXPORT_ZERO": 0,
+	}
+)
+
+func (x LocalMessage_NestedExportEnum) Enum() *LocalMessage_NestedExportEnum {
+	p := new(LocalMessage_NestedExportEnum)
+	*p = x
+	return p
+}
+
+func (x LocalMessage_NestedExportEnum) String() string {
+	return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x))
+}
+
+func (LocalMessage_NestedExportEnum) Descriptor() protoreflect.EnumDescriptor {
+	return file_cmd_protoc_gen_go_testdata_visibility_visibility_proto_enumTypes[10].Descriptor()
+}
+
+func (LocalMessage_NestedExportEnum) Type() protoreflect.EnumType {
+	return &file_cmd_protoc_gen_go_testdata_visibility_visibility_proto_enumTypes[10]
+}
+
+func (x LocalMessage_NestedExportEnum) Number() protoreflect.EnumNumber {
+	return protoreflect.EnumNumber(x)
+}
+
+type LocalMessage_NestedLocalEnum int32
+
+const (
+	LocalMessage_LOCAL_ZERO LocalMessage_NestedLocalEnum = 0
+)
+
+// Enum value maps for LocalMessage_NestedLocalEnum.
+var (
+	LocalMessage_NestedLocalEnum_name = map[int32]string{
+		0: "LOCAL_ZERO",
+	}
+	LocalMessage_NestedLocalEnum_value = map[string]int32{
+		"LOCAL_ZERO": 0,
+	}
+)
+
+func (x LocalMessage_NestedLocalEnum) Enum() *LocalMessage_NestedLocalEnum {
+	p := new(LocalMessage_NestedLocalEnum)
+	*p = x
+	return p
+}
+
+func (x LocalMessage_NestedLocalEnum) String() string {
+	return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x))
+}
+
+func (LocalMessage_NestedLocalEnum) Descriptor() protoreflect.EnumDescriptor {
+	return file_cmd_protoc_gen_go_testdata_visibility_visibility_proto_enumTypes[11].Descriptor()
+}
+
+func (LocalMessage_NestedLocalEnum) Type() protoreflect.EnumType {
+	return &file_cmd_protoc_gen_go_testdata_visibility_visibility_proto_enumTypes[11]
+}
+
+func (x LocalMessage_NestedLocalEnum) Number() protoreflect.EnumNumber {
+	return protoreflect.EnumNumber(x)
+}
+
+type DefaultMessage struct {
+	state         protoimpl.MessageState `protogen:"opaque.v1"`
+	unknownFields protoimpl.UnknownFields
+	sizeCache     protoimpl.SizeCache
+}
+
+func (x *DefaultMessage) Reset() {
+	*x = DefaultMessage{}
+	mi := &file_cmd_protoc_gen_go_testdata_visibility_visibility_proto_msgTypes[0]
+	ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+	ms.StoreMessageInfo(mi)
+}
+
+func (x *DefaultMessage) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*DefaultMessage) ProtoMessage() {}
+
+func (x *DefaultMessage) ProtoReflect() protoreflect.Message {
+	mi := &file_cmd_protoc_gen_go_testdata_visibility_visibility_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)
+}
+
+type DefaultMessage_builder struct {
+	_ [0]func() // Prevents comparability and use of unkeyed literals for the builder.
+
+}
+
+func (b0 DefaultMessage_builder) Build() *DefaultMessage {
+	m0 := &DefaultMessage{}
+	b, x := &b0, m0
+	_, _ = b, x
+	return m0
+}
+
+type ExportMessage struct {
+	state         protoimpl.MessageState `protogen:"opaque.v1"`
+	unknownFields protoimpl.UnknownFields
+	sizeCache     protoimpl.SizeCache
+}
+
+func (x *ExportMessage) Reset() {
+	*x = ExportMessage{}
+	mi := &file_cmd_protoc_gen_go_testdata_visibility_visibility_proto_msgTypes[1]
+	ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+	ms.StoreMessageInfo(mi)
+}
+
+func (x *ExportMessage) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*ExportMessage) ProtoMessage() {}
+
+func (x *ExportMessage) ProtoReflect() protoreflect.Message {
+	mi := &file_cmd_protoc_gen_go_testdata_visibility_visibility_proto_msgTypes[1]
+	if x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+type ExportMessage_builder struct {
+	_ [0]func() // Prevents comparability and use of unkeyed literals for the builder.
+
+}
+
+func (b0 ExportMessage_builder) Build() *ExportMessage {
+	m0 := &ExportMessage{}
+	b, x := &b0, m0
+	_, _ = b, x
+	return m0
+}
+
+type LocalMessage struct {
+	state         protoimpl.MessageState `protogen:"opaque.v1"`
+	unknownFields protoimpl.UnknownFields
+	sizeCache     protoimpl.SizeCache
+}
+
+func (x *LocalMessage) Reset() {
+	*x = LocalMessage{}
+	mi := &file_cmd_protoc_gen_go_testdata_visibility_visibility_proto_msgTypes[2]
+	ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+	ms.StoreMessageInfo(mi)
+}
+
+func (x *LocalMessage) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*LocalMessage) ProtoMessage() {}
+
+func (x *LocalMessage) ProtoReflect() protoreflect.Message {
+	mi := &file_cmd_protoc_gen_go_testdata_visibility_visibility_proto_msgTypes[2]
+	if x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+type LocalMessage_builder struct {
+	_ [0]func() // Prevents comparability and use of unkeyed literals for the builder.
+
+}
+
+func (b0 LocalMessage_builder) Build() *LocalMessage {
+	m0 := &LocalMessage{}
+	b, x := &b0, m0
+	_, _ = b, x
+	return m0
+}
+
+type DefaultMessage_NestedDefaultMessage struct {
+	state                  protoimpl.MessageState `protogen:"opaque.v1"`
+	xxx_hidden_Id          uint64                 `protobuf:"varint,1,opt,name=id"`
+	XXX_raceDetectHookData protoimpl.RaceDetectHookData
+	XXX_presence           [1]uint32
+	unknownFields          protoimpl.UnknownFields
+	sizeCache              protoimpl.SizeCache
+}
+
+func (x *DefaultMessage_NestedDefaultMessage) Reset() {
+	*x = DefaultMessage_NestedDefaultMessage{}
+	mi := &file_cmd_protoc_gen_go_testdata_visibility_visibility_proto_msgTypes[3]
+	ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+	ms.StoreMessageInfo(mi)
+}
+
+func (x *DefaultMessage_NestedDefaultMessage) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*DefaultMessage_NestedDefaultMessage) ProtoMessage() {}
+
+func (x *DefaultMessage_NestedDefaultMessage) ProtoReflect() protoreflect.Message {
+	mi := &file_cmd_protoc_gen_go_testdata_visibility_visibility_proto_msgTypes[3]
+	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 *DefaultMessage_NestedDefaultMessage) GetId() uint64 {
+	if x != nil {
+		return x.xxx_hidden_Id
+	}
+	return 0
+}
+
+func (x *DefaultMessage_NestedDefaultMessage) SetId(v uint64) {
+	x.xxx_hidden_Id = v
+	protoimpl.X.SetPresent(&(x.XXX_presence[0]), 0, 1)
+}
+
+func (x *DefaultMessage_NestedDefaultMessage) HasId() bool {
+	if x == nil {
+		return false
+	}
+	return protoimpl.X.Present(&(x.XXX_presence[0]), 0)
+}
+
+func (x *DefaultMessage_NestedDefaultMessage) ClearId() {
+	protoimpl.X.ClearPresent(&(x.XXX_presence[0]), 0)
+	x.xxx_hidden_Id = 0
+}
+
+type DefaultMessage_NestedDefaultMessage_builder struct {
+	_ [0]func() // Prevents comparability and use of unkeyed literals for the builder.
+
+	Id *uint64
+}
+
+func (b0 DefaultMessage_NestedDefaultMessage_builder) Build() *DefaultMessage_NestedDefaultMessage {
+	m0 := &DefaultMessage_NestedDefaultMessage{}
+	b, x := &b0, m0
+	_, _ = b, x
+	if b.Id != nil {
+		protoimpl.X.SetPresentNonAtomic(&(x.XXX_presence[0]), 0, 1)
+		x.xxx_hidden_Id = *b.Id
+	}
+	return m0
+}
+
+type DefaultMessage_NestedExportMessage struct {
+	state                  protoimpl.MessageState `protogen:"opaque.v1"`
+	xxx_hidden_Name        *string                `protobuf:"bytes,1,opt,name=name"`
+	XXX_raceDetectHookData protoimpl.RaceDetectHookData
+	XXX_presence           [1]uint32
+	unknownFields          protoimpl.UnknownFields
+	sizeCache              protoimpl.SizeCache
+}
+
+func (x *DefaultMessage_NestedExportMessage) Reset() {
+	*x = DefaultMessage_NestedExportMessage{}
+	mi := &file_cmd_protoc_gen_go_testdata_visibility_visibility_proto_msgTypes[4]
+	ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+	ms.StoreMessageInfo(mi)
+}
+
+func (x *DefaultMessage_NestedExportMessage) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*DefaultMessage_NestedExportMessage) ProtoMessage() {}
+
+func (x *DefaultMessage_NestedExportMessage) ProtoReflect() protoreflect.Message {
+	mi := &file_cmd_protoc_gen_go_testdata_visibility_visibility_proto_msgTypes[4]
+	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 *DefaultMessage_NestedExportMessage) GetName() string {
+	if x != nil {
+		if x.xxx_hidden_Name != nil {
+			return *x.xxx_hidden_Name
+		}
+		return ""
+	}
+	return ""
+}
+
+func (x *DefaultMessage_NestedExportMessage) SetName(v string) {
+	x.xxx_hidden_Name = &v
+	protoimpl.X.SetPresent(&(x.XXX_presence[0]), 0, 1)
+}
+
+func (x *DefaultMessage_NestedExportMessage) HasName() bool {
+	if x == nil {
+		return false
+	}
+	return protoimpl.X.Present(&(x.XXX_presence[0]), 0)
+}
+
+func (x *DefaultMessage_NestedExportMessage) ClearName() {
+	protoimpl.X.ClearPresent(&(x.XXX_presence[0]), 0)
+	x.xxx_hidden_Name = nil
+}
+
+type DefaultMessage_NestedExportMessage_builder struct {
+	_ [0]func() // Prevents comparability and use of unkeyed literals for the builder.
+
+	Name *string
+}
+
+func (b0 DefaultMessage_NestedExportMessage_builder) Build() *DefaultMessage_NestedExportMessage {
+	m0 := &DefaultMessage_NestedExportMessage{}
+	b, x := &b0, m0
+	_, _ = b, x
+	if b.Name != nil {
+		protoimpl.X.SetPresentNonAtomic(&(x.XXX_presence[0]), 0, 1)
+		x.xxx_hidden_Name = b.Name
+	}
+	return m0
+}
+
+type DefaultMessage_NestedLocalMessage struct {
+	state                  protoimpl.MessageState `protogen:"opaque.v1"`
+	xxx_hidden_Uuid        []byte                 `protobuf:"bytes,1,opt,name=uuid"`
+	XXX_raceDetectHookData protoimpl.RaceDetectHookData
+	XXX_presence           [1]uint32
+	unknownFields          protoimpl.UnknownFields
+	sizeCache              protoimpl.SizeCache
+}
+
+func (x *DefaultMessage_NestedLocalMessage) Reset() {
+	*x = DefaultMessage_NestedLocalMessage{}
+	mi := &file_cmd_protoc_gen_go_testdata_visibility_visibility_proto_msgTypes[5]
+	ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+	ms.StoreMessageInfo(mi)
+}
+
+func (x *DefaultMessage_NestedLocalMessage) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*DefaultMessage_NestedLocalMessage) ProtoMessage() {}
+
+func (x *DefaultMessage_NestedLocalMessage) ProtoReflect() protoreflect.Message {
+	mi := &file_cmd_protoc_gen_go_testdata_visibility_visibility_proto_msgTypes[5]
+	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 *DefaultMessage_NestedLocalMessage) GetUuid() []byte {
+	if x != nil {
+		return x.xxx_hidden_Uuid
+	}
+	return nil
+}
+
+func (x *DefaultMessage_NestedLocalMessage) SetUuid(v []byte) {
+	if v == nil {
+		v = []byte{}
+	}
+	x.xxx_hidden_Uuid = v
+	protoimpl.X.SetPresent(&(x.XXX_presence[0]), 0, 1)
+}
+
+func (x *DefaultMessage_NestedLocalMessage) HasUuid() bool {
+	if x == nil {
+		return false
+	}
+	return protoimpl.X.Present(&(x.XXX_presence[0]), 0)
+}
+
+func (x *DefaultMessage_NestedLocalMessage) ClearUuid() {
+	protoimpl.X.ClearPresent(&(x.XXX_presence[0]), 0)
+	x.xxx_hidden_Uuid = nil
+}
+
+type DefaultMessage_NestedLocalMessage_builder struct {
+	_ [0]func() // Prevents comparability and use of unkeyed literals for the builder.
+
+	Uuid []byte
+}
+
+func (b0 DefaultMessage_NestedLocalMessage_builder) Build() *DefaultMessage_NestedLocalMessage {
+	m0 := &DefaultMessage_NestedLocalMessage{}
+	b, x := &b0, m0
+	_, _ = b, x
+	if b.Uuid != nil {
+		protoimpl.X.SetPresentNonAtomic(&(x.XXX_presence[0]), 0, 1)
+		x.xxx_hidden_Uuid = b.Uuid
+	}
+	return m0
+}
+
+type ExportMessage_NestedDefaultMessage struct {
+	state                  protoimpl.MessageState `protogen:"opaque.v1"`
+	xxx_hidden_Id          uint64                 `protobuf:"varint,1,opt,name=id"`
+	XXX_raceDetectHookData protoimpl.RaceDetectHookData
+	XXX_presence           [1]uint32
+	unknownFields          protoimpl.UnknownFields
+	sizeCache              protoimpl.SizeCache
+}
+
+func (x *ExportMessage_NestedDefaultMessage) Reset() {
+	*x = ExportMessage_NestedDefaultMessage{}
+	mi := &file_cmd_protoc_gen_go_testdata_visibility_visibility_proto_msgTypes[6]
+	ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+	ms.StoreMessageInfo(mi)
+}
+
+func (x *ExportMessage_NestedDefaultMessage) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*ExportMessage_NestedDefaultMessage) ProtoMessage() {}
+
+func (x *ExportMessage_NestedDefaultMessage) ProtoReflect() protoreflect.Message {
+	mi := &file_cmd_protoc_gen_go_testdata_visibility_visibility_proto_msgTypes[6]
+	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 *ExportMessage_NestedDefaultMessage) GetId() uint64 {
+	if x != nil {
+		return x.xxx_hidden_Id
+	}
+	return 0
+}
+
+func (x *ExportMessage_NestedDefaultMessage) SetId(v uint64) {
+	x.xxx_hidden_Id = v
+	protoimpl.X.SetPresent(&(x.XXX_presence[0]), 0, 1)
+}
+
+func (x *ExportMessage_NestedDefaultMessage) HasId() bool {
+	if x == nil {
+		return false
+	}
+	return protoimpl.X.Present(&(x.XXX_presence[0]), 0)
+}
+
+func (x *ExportMessage_NestedDefaultMessage) ClearId() {
+	protoimpl.X.ClearPresent(&(x.XXX_presence[0]), 0)
+	x.xxx_hidden_Id = 0
+}
+
+type ExportMessage_NestedDefaultMessage_builder struct {
+	_ [0]func() // Prevents comparability and use of unkeyed literals for the builder.
+
+	Id *uint64
+}
+
+func (b0 ExportMessage_NestedDefaultMessage_builder) Build() *ExportMessage_NestedDefaultMessage {
+	m0 := &ExportMessage_NestedDefaultMessage{}
+	b, x := &b0, m0
+	_, _ = b, x
+	if b.Id != nil {
+		protoimpl.X.SetPresentNonAtomic(&(x.XXX_presence[0]), 0, 1)
+		x.xxx_hidden_Id = *b.Id
+	}
+	return m0
+}
+
+type ExportMessage_NestedExportMessage struct {
+	state                  protoimpl.MessageState `protogen:"opaque.v1"`
+	xxx_hidden_Name        *string                `protobuf:"bytes,1,opt,name=name"`
+	XXX_raceDetectHookData protoimpl.RaceDetectHookData
+	XXX_presence           [1]uint32
+	unknownFields          protoimpl.UnknownFields
+	sizeCache              protoimpl.SizeCache
+}
+
+func (x *ExportMessage_NestedExportMessage) Reset() {
+	*x = ExportMessage_NestedExportMessage{}
+	mi := &file_cmd_protoc_gen_go_testdata_visibility_visibility_proto_msgTypes[7]
+	ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+	ms.StoreMessageInfo(mi)
+}
+
+func (x *ExportMessage_NestedExportMessage) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*ExportMessage_NestedExportMessage) ProtoMessage() {}
+
+func (x *ExportMessage_NestedExportMessage) ProtoReflect() protoreflect.Message {
+	mi := &file_cmd_protoc_gen_go_testdata_visibility_visibility_proto_msgTypes[7]
+	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 *ExportMessage_NestedExportMessage) GetName() string {
+	if x != nil {
+		if x.xxx_hidden_Name != nil {
+			return *x.xxx_hidden_Name
+		}
+		return ""
+	}
+	return ""
+}
+
+func (x *ExportMessage_NestedExportMessage) SetName(v string) {
+	x.xxx_hidden_Name = &v
+	protoimpl.X.SetPresent(&(x.XXX_presence[0]), 0, 1)
+}
+
+func (x *ExportMessage_NestedExportMessage) HasName() bool {
+	if x == nil {
+		return false
+	}
+	return protoimpl.X.Present(&(x.XXX_presence[0]), 0)
+}
+
+func (x *ExportMessage_NestedExportMessage) ClearName() {
+	protoimpl.X.ClearPresent(&(x.XXX_presence[0]), 0)
+	x.xxx_hidden_Name = nil
+}
+
+type ExportMessage_NestedExportMessage_builder struct {
+	_ [0]func() // Prevents comparability and use of unkeyed literals for the builder.
+
+	Name *string
+}
+
+func (b0 ExportMessage_NestedExportMessage_builder) Build() *ExportMessage_NestedExportMessage {
+	m0 := &ExportMessage_NestedExportMessage{}
+	b, x := &b0, m0
+	_, _ = b, x
+	if b.Name != nil {
+		protoimpl.X.SetPresentNonAtomic(&(x.XXX_presence[0]), 0, 1)
+		x.xxx_hidden_Name = b.Name
+	}
+	return m0
+}
+
+type ExportMessage_NestedLocalMessage struct {
+	state                  protoimpl.MessageState `protogen:"opaque.v1"`
+	xxx_hidden_Uuid        []byte                 `protobuf:"bytes,1,opt,name=uuid"`
+	XXX_raceDetectHookData protoimpl.RaceDetectHookData
+	XXX_presence           [1]uint32
+	unknownFields          protoimpl.UnknownFields
+	sizeCache              protoimpl.SizeCache
+}
+
+func (x *ExportMessage_NestedLocalMessage) Reset() {
+	*x = ExportMessage_NestedLocalMessage{}
+	mi := &file_cmd_protoc_gen_go_testdata_visibility_visibility_proto_msgTypes[8]
+	ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+	ms.StoreMessageInfo(mi)
+}
+
+func (x *ExportMessage_NestedLocalMessage) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*ExportMessage_NestedLocalMessage) ProtoMessage() {}
+
+func (x *ExportMessage_NestedLocalMessage) ProtoReflect() protoreflect.Message {
+	mi := &file_cmd_protoc_gen_go_testdata_visibility_visibility_proto_msgTypes[8]
+	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 *ExportMessage_NestedLocalMessage) GetUuid() []byte {
+	if x != nil {
+		return x.xxx_hidden_Uuid
+	}
+	return nil
+}
+
+func (x *ExportMessage_NestedLocalMessage) SetUuid(v []byte) {
+	if v == nil {
+		v = []byte{}
+	}
+	x.xxx_hidden_Uuid = v
+	protoimpl.X.SetPresent(&(x.XXX_presence[0]), 0, 1)
+}
+
+func (x *ExportMessage_NestedLocalMessage) HasUuid() bool {
+	if x == nil {
+		return false
+	}
+	return protoimpl.X.Present(&(x.XXX_presence[0]), 0)
+}
+
+func (x *ExportMessage_NestedLocalMessage) ClearUuid() {
+	protoimpl.X.ClearPresent(&(x.XXX_presence[0]), 0)
+	x.xxx_hidden_Uuid = nil
+}
+
+type ExportMessage_NestedLocalMessage_builder struct {
+	_ [0]func() // Prevents comparability and use of unkeyed literals for the builder.
+
+	Uuid []byte
+}
+
+func (b0 ExportMessage_NestedLocalMessage_builder) Build() *ExportMessage_NestedLocalMessage {
+	m0 := &ExportMessage_NestedLocalMessage{}
+	b, x := &b0, m0
+	_, _ = b, x
+	if b.Uuid != nil {
+		protoimpl.X.SetPresentNonAtomic(&(x.XXX_presence[0]), 0, 1)
+		x.xxx_hidden_Uuid = b.Uuid
+	}
+	return m0
+}
+
+type LocalMessage_NestedDefaultMessage struct {
+	state                  protoimpl.MessageState `protogen:"opaque.v1"`
+	xxx_hidden_Id          uint64                 `protobuf:"varint,1,opt,name=id"`
+	XXX_raceDetectHookData protoimpl.RaceDetectHookData
+	XXX_presence           [1]uint32
+	unknownFields          protoimpl.UnknownFields
+	sizeCache              protoimpl.SizeCache
+}
+
+func (x *LocalMessage_NestedDefaultMessage) Reset() {
+	*x = LocalMessage_NestedDefaultMessage{}
+	mi := &file_cmd_protoc_gen_go_testdata_visibility_visibility_proto_msgTypes[9]
+	ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+	ms.StoreMessageInfo(mi)
+}
+
+func (x *LocalMessage_NestedDefaultMessage) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*LocalMessage_NestedDefaultMessage) ProtoMessage() {}
+
+func (x *LocalMessage_NestedDefaultMessage) ProtoReflect() protoreflect.Message {
+	mi := &file_cmd_protoc_gen_go_testdata_visibility_visibility_proto_msgTypes[9]
+	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 *LocalMessage_NestedDefaultMessage) GetId() uint64 {
+	if x != nil {
+		return x.xxx_hidden_Id
+	}
+	return 0
+}
+
+func (x *LocalMessage_NestedDefaultMessage) SetId(v uint64) {
+	x.xxx_hidden_Id = v
+	protoimpl.X.SetPresent(&(x.XXX_presence[0]), 0, 1)
+}
+
+func (x *LocalMessage_NestedDefaultMessage) HasId() bool {
+	if x == nil {
+		return false
+	}
+	return protoimpl.X.Present(&(x.XXX_presence[0]), 0)
+}
+
+func (x *LocalMessage_NestedDefaultMessage) ClearId() {
+	protoimpl.X.ClearPresent(&(x.XXX_presence[0]), 0)
+	x.xxx_hidden_Id = 0
+}
+
+type LocalMessage_NestedDefaultMessage_builder struct {
+	_ [0]func() // Prevents comparability and use of unkeyed literals for the builder.
+
+	Id *uint64
+}
+
+func (b0 LocalMessage_NestedDefaultMessage_builder) Build() *LocalMessage_NestedDefaultMessage {
+	m0 := &LocalMessage_NestedDefaultMessage{}
+	b, x := &b0, m0
+	_, _ = b, x
+	if b.Id != nil {
+		protoimpl.X.SetPresentNonAtomic(&(x.XXX_presence[0]), 0, 1)
+		x.xxx_hidden_Id = *b.Id
+	}
+	return m0
+}
+
+type LocalMessage_NestedExportMessage struct {
+	state                  protoimpl.MessageState `protogen:"opaque.v1"`
+	xxx_hidden_Name        *string                `protobuf:"bytes,1,opt,name=name"`
+	XXX_raceDetectHookData protoimpl.RaceDetectHookData
+	XXX_presence           [1]uint32
+	unknownFields          protoimpl.UnknownFields
+	sizeCache              protoimpl.SizeCache
+}
+
+func (x *LocalMessage_NestedExportMessage) Reset() {
+	*x = LocalMessage_NestedExportMessage{}
+	mi := &file_cmd_protoc_gen_go_testdata_visibility_visibility_proto_msgTypes[10]
+	ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+	ms.StoreMessageInfo(mi)
+}
+
+func (x *LocalMessage_NestedExportMessage) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*LocalMessage_NestedExportMessage) ProtoMessage() {}
+
+func (x *LocalMessage_NestedExportMessage) ProtoReflect() protoreflect.Message {
+	mi := &file_cmd_protoc_gen_go_testdata_visibility_visibility_proto_msgTypes[10]
+	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 *LocalMessage_NestedExportMessage) GetName() string {
+	if x != nil {
+		if x.xxx_hidden_Name != nil {
+			return *x.xxx_hidden_Name
+		}
+		return ""
+	}
+	return ""
+}
+
+func (x *LocalMessage_NestedExportMessage) SetName(v string) {
+	x.xxx_hidden_Name = &v
+	protoimpl.X.SetPresent(&(x.XXX_presence[0]), 0, 1)
+}
+
+func (x *LocalMessage_NestedExportMessage) HasName() bool {
+	if x == nil {
+		return false
+	}
+	return protoimpl.X.Present(&(x.XXX_presence[0]), 0)
+}
+
+func (x *LocalMessage_NestedExportMessage) ClearName() {
+	protoimpl.X.ClearPresent(&(x.XXX_presence[0]), 0)
+	x.xxx_hidden_Name = nil
+}
+
+type LocalMessage_NestedExportMessage_builder struct {
+	_ [0]func() // Prevents comparability and use of unkeyed literals for the builder.
+
+	Name *string
+}
+
+func (b0 LocalMessage_NestedExportMessage_builder) Build() *LocalMessage_NestedExportMessage {
+	m0 := &LocalMessage_NestedExportMessage{}
+	b, x := &b0, m0
+	_, _ = b, x
+	if b.Name != nil {
+		protoimpl.X.SetPresentNonAtomic(&(x.XXX_presence[0]), 0, 1)
+		x.xxx_hidden_Name = b.Name
+	}
+	return m0
+}
+
+type LocalMessage_NestedLocalMessage struct {
+	state                  protoimpl.MessageState `protogen:"opaque.v1"`
+	xxx_hidden_Uuid        []byte                 `protobuf:"bytes,1,opt,name=uuid"`
+	XXX_raceDetectHookData protoimpl.RaceDetectHookData
+	XXX_presence           [1]uint32
+	unknownFields          protoimpl.UnknownFields
+	sizeCache              protoimpl.SizeCache
+}
+
+func (x *LocalMessage_NestedLocalMessage) Reset() {
+	*x = LocalMessage_NestedLocalMessage{}
+	mi := &file_cmd_protoc_gen_go_testdata_visibility_visibility_proto_msgTypes[11]
+	ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+	ms.StoreMessageInfo(mi)
+}
+
+func (x *LocalMessage_NestedLocalMessage) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*LocalMessage_NestedLocalMessage) ProtoMessage() {}
+
+func (x *LocalMessage_NestedLocalMessage) ProtoReflect() protoreflect.Message {
+	mi := &file_cmd_protoc_gen_go_testdata_visibility_visibility_proto_msgTypes[11]
+	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 *LocalMessage_NestedLocalMessage) GetUuid() []byte {
+	if x != nil {
+		return x.xxx_hidden_Uuid
+	}
+	return nil
+}
+
+func (x *LocalMessage_NestedLocalMessage) SetUuid(v []byte) {
+	if v == nil {
+		v = []byte{}
+	}
+	x.xxx_hidden_Uuid = v
+	protoimpl.X.SetPresent(&(x.XXX_presence[0]), 0, 1)
+}
+
+func (x *LocalMessage_NestedLocalMessage) HasUuid() bool {
+	if x == nil {
+		return false
+	}
+	return protoimpl.X.Present(&(x.XXX_presence[0]), 0)
+}
+
+func (x *LocalMessage_NestedLocalMessage) ClearUuid() {
+	protoimpl.X.ClearPresent(&(x.XXX_presence[0]), 0)
+	x.xxx_hidden_Uuid = nil
+}
+
+type LocalMessage_NestedLocalMessage_builder struct {
+	_ [0]func() // Prevents comparability and use of unkeyed literals for the builder.
+
+	Uuid []byte
+}
+
+func (b0 LocalMessage_NestedLocalMessage_builder) Build() *LocalMessage_NestedLocalMessage {
+	m0 := &LocalMessage_NestedLocalMessage{}
+	b, x := &b0, m0
+	_, _ = b, x
+	if b.Uuid != nil {
+		protoimpl.X.SetPresentNonAtomic(&(x.XXX_presence[0]), 0, 1)
+		x.xxx_hidden_Uuid = b.Uuid
+	}
+	return m0
+}
+
+var File_cmd_protoc_gen_go_testdata_visibility_visibility_proto protoreflect.FileDescriptor
+
+const file_cmd_protoc_gen_go_testdata_visibility_visibility_proto_rawDesc = "" +
+	"\n" +
+	"6cmd/protoc-gen-go/testdata/visibility/visibility.proto\x12\x19goproto.protoc.visibility\"\x84\x02\n" +
+	"\x0eDefaultMessage\x1a&\n" +
+	"\x14NestedDefaultMessage\x12\x0e\n" +
+	"\x02id\x18\x01 \x01(\x04R\x02id\x1a+\n" +
+	"\x13NestedExportMessage\x12\x12\n" +
+	"\x04name\x18\x01 \x01(\tR\x04nameX\x02\x1a*\n" +
+	"\x12NestedLocalMessage\x12\x12\n" +
+	"\x04uuid\x18\x01 \x01(\fR\x04uuidX\x01\"%\n" +
+	"\x11NestedDefaultEnum\x12\x10\n" +
+	"\fDEFAULT_ZERO\x10\x00\"%\n" +
+	"\x10NestedExportEnum\x12\x0f\n" +
+	"\vEXPORT_ZERO\x10\x000\x02\"#\n" +
+	"\x0fNestedLocalEnum\x12\x0e\n" +
+	"\n" +
+	"LOCAL_ZERO\x10\x000\x01\"\x85\x02\n" +
+	"\rExportMessage\x1a&\n" +
+	"\x14NestedDefaultMessage\x12\x0e\n" +
+	"\x02id\x18\x01 \x01(\x04R\x02id\x1a+\n" +
+	"\x13NestedExportMessage\x12\x12\n" +
+	"\x04name\x18\x01 \x01(\tR\x04nameX\x02\x1a*\n" +
+	"\x12NestedLocalMessage\x12\x12\n" +
+	"\x04uuid\x18\x01 \x01(\fR\x04uuidX\x01\"%\n" +
+	"\x11NestedDefaultEnum\x12\x10\n" +
+	"\fDEFAULT_ZERO\x10\x00\"%\n" +
+	"\x10NestedExportEnum\x12\x0f\n" +
+	"\vEXPORT_ZERO\x10\x000\x02\"#\n" +
+	"\x0fNestedLocalEnum\x12\x0e\n" +
+	"\n" +
+	"LOCAL_ZERO\x10\x000\x01X\x02\"\x84\x02\n" +
+	"\fLocalMessage\x1a&\n" +
+	"\x14NestedDefaultMessage\x12\x0e\n" +
+	"\x02id\x18\x01 \x01(\x04R\x02id\x1a+\n" +
+	"\x13NestedExportMessage\x12\x12\n" +
+	"\x04name\x18\x01 \x01(\tR\x04nameX\x02\x1a*\n" +
+	"\x12NestedLocalMessage\x12\x12\n" +
+	"\x04uuid\x18\x01 \x01(\fR\x04uuidX\x01\"%\n" +
+	"\x11NestedDefaultEnum\x12\x10\n" +
+	"\fDEFAULT_ZERO\x10\x00\"%\n" +
+	"\x10NestedExportEnum\x12\x0f\n" +
+	"\vEXPORT_ZERO\x10\x000\x02\"#\n" +
+	"\x0fNestedLocalEnum\x12\x0e\n" +
+	"\n" +
+	"LOCAL_ZERO\x10\x000\x01X\x01*\x1f\n" +
+	"\vDefaultEnum\x12\x10\n" +
+	"\fDEFAULT_ZERO\x10\x00*\x1f\n" +
+	"\n" +
+	"ExportEnum\x12\x0f\n" +
+	"\vEXPORT_ZERO\x10\x000\x02*\x1d\n" +
+	"\tLocalEnum\x12\x0e\n" +
+	"\n" +
+	"LOCAL_ZERO\x10\x000\x01BEZ@google.golang.org/protobuf/cmd/protoc-gen-go/testdata/visibility\x92\x03\x00b\beditionsp\xe9\a"
+
+var file_cmd_protoc_gen_go_testdata_visibility_visibility_proto_enumTypes = make([]protoimpl.EnumInfo, 12)
+var file_cmd_protoc_gen_go_testdata_visibility_visibility_proto_msgTypes = make([]protoimpl.MessageInfo, 12)
+var file_cmd_protoc_gen_go_testdata_visibility_visibility_proto_goTypes = []any{
+	(DefaultEnum)(0),                            // 0: goproto.protoc.visibility.DefaultEnum
+	(ExportEnum)(0),                             // 1: goproto.protoc.visibility.ExportEnum
+	(LocalEnum)(0),                              // 2: goproto.protoc.visibility.LocalEnum
+	(DefaultMessage_NestedDefaultEnum)(0),       // 3: goproto.protoc.visibility.DefaultMessage.NestedDefaultEnum
+	(DefaultMessage_NestedExportEnum)(0),        // 4: goproto.protoc.visibility.DefaultMessage.NestedExportEnum
+	(DefaultMessage_NestedLocalEnum)(0),         // 5: goproto.protoc.visibility.DefaultMessage.NestedLocalEnum
+	(ExportMessage_NestedDefaultEnum)(0),        // 6: goproto.protoc.visibility.ExportMessage.NestedDefaultEnum
+	(ExportMessage_NestedExportEnum)(0),         // 7: goproto.protoc.visibility.ExportMessage.NestedExportEnum
+	(ExportMessage_NestedLocalEnum)(0),          // 8: goproto.protoc.visibility.ExportMessage.NestedLocalEnum
+	(LocalMessage_NestedDefaultEnum)(0),         // 9: goproto.protoc.visibility.LocalMessage.NestedDefaultEnum
+	(LocalMessage_NestedExportEnum)(0),          // 10: goproto.protoc.visibility.LocalMessage.NestedExportEnum
+	(LocalMessage_NestedLocalEnum)(0),           // 11: goproto.protoc.visibility.LocalMessage.NestedLocalEnum
+	(*DefaultMessage)(nil),                      // 12: goproto.protoc.visibility.DefaultMessage
+	(*ExportMessage)(nil),                       // 13: goproto.protoc.visibility.ExportMessage
+	(*LocalMessage)(nil),                        // 14: goproto.protoc.visibility.LocalMessage
+	(*DefaultMessage_NestedDefaultMessage)(nil), // 15: goproto.protoc.visibility.DefaultMessage.NestedDefaultMessage
+	(*DefaultMessage_NestedExportMessage)(nil),  // 16: goproto.protoc.visibility.DefaultMessage.NestedExportMessage
+	(*DefaultMessage_NestedLocalMessage)(nil),   // 17: goproto.protoc.visibility.DefaultMessage.NestedLocalMessage
+	(*ExportMessage_NestedDefaultMessage)(nil),  // 18: goproto.protoc.visibility.ExportMessage.NestedDefaultMessage
+	(*ExportMessage_NestedExportMessage)(nil),   // 19: goproto.protoc.visibility.ExportMessage.NestedExportMessage
+	(*ExportMessage_NestedLocalMessage)(nil),    // 20: goproto.protoc.visibility.ExportMessage.NestedLocalMessage
+	(*LocalMessage_NestedDefaultMessage)(nil),   // 21: goproto.protoc.visibility.LocalMessage.NestedDefaultMessage
+	(*LocalMessage_NestedExportMessage)(nil),    // 22: goproto.protoc.visibility.LocalMessage.NestedExportMessage
+	(*LocalMessage_NestedLocalMessage)(nil),     // 23: goproto.protoc.visibility.LocalMessage.NestedLocalMessage
+}
+var file_cmd_protoc_gen_go_testdata_visibility_visibility_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_visibility_visibility_proto_init() }
+func file_cmd_protoc_gen_go_testdata_visibility_visibility_proto_init() {
+	if File_cmd_protoc_gen_go_testdata_visibility_visibility_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_visibility_visibility_proto_rawDesc), len(file_cmd_protoc_gen_go_testdata_visibility_visibility_proto_rawDesc)),
+			NumEnums:      12,
+			NumMessages:   12,
+			NumExtensions: 0,
+			NumServices:   0,
+		},
+		GoTypes:           file_cmd_protoc_gen_go_testdata_visibility_visibility_proto_goTypes,
+		DependencyIndexes: file_cmd_protoc_gen_go_testdata_visibility_visibility_proto_depIdxs,
+		EnumInfos:         file_cmd_protoc_gen_go_testdata_visibility_visibility_proto_enumTypes,
+		MessageInfos:      file_cmd_protoc_gen_go_testdata_visibility_visibility_proto_msgTypes,
+	}.Build()
+	File_cmd_protoc_gen_go_testdata_visibility_visibility_proto = out.File
+	file_cmd_protoc_gen_go_testdata_visibility_visibility_proto_goTypes = nil
+	file_cmd_protoc_gen_go_testdata_visibility_visibility_proto_depIdxs = nil
+}
diff --git a/cmd/protoc-gen-go/testdata/visibility/visibility.proto b/cmd/protoc-gen-go/testdata/visibility/visibility.proto
new file mode 100644
index 0000000..1707062
--- /dev/null
+++ b/cmd/protoc-gen-go/testdata/visibility/visibility.proto
@@ -0,0 +1,85 @@
+// Copyright 2020 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 goproto.protoc.visibility;
+
+option features.default_symbol_visibility = LOCAL_ALL;
+option go_package = "google.golang.org/protobuf/cmd/protoc-gen-go/testdata/visibility";
+
+message DefaultMessage {
+  message NestedDefaultMessage {
+    uint64 id = 1;
+  }
+  enum NestedDefaultEnum {
+    DEFAULT_ZERO = 0;
+  }
+  export message NestedExportMessage {
+    string name = 1;
+  }
+  export enum NestedExportEnum {
+    EXPORT_ZERO = 0;
+  }
+  local message NestedLocalMessage {
+    bytes uuid = 1;
+  }
+  local enum NestedLocalEnum {
+    LOCAL_ZERO = 0;
+  }
+}
+
+export message ExportMessage {
+  message NestedDefaultMessage {
+    uint64 id = 1;
+  }
+  enum NestedDefaultEnum {
+    DEFAULT_ZERO = 0;
+  }
+  export message NestedExportMessage {
+    string name = 1;
+  }
+  export enum NestedExportEnum {
+    EXPORT_ZERO = 0;
+  }
+  local message NestedLocalMessage {
+    bytes uuid = 1;
+  }
+  local enum NestedLocalEnum {
+    LOCAL_ZERO = 0;
+  }
+}
+
+local message LocalMessage {
+  message NestedDefaultMessage {
+    uint64 id = 1;
+  }
+  enum NestedDefaultEnum {
+    DEFAULT_ZERO = 0;
+  }
+  export message NestedExportMessage {
+    string name = 1;
+  }
+  export enum NestedExportEnum {
+    EXPORT_ZERO = 0;
+  }
+  local message NestedLocalMessage {
+    bytes uuid = 1;
+  }
+  local enum NestedLocalEnum {
+    LOCAL_ZERO = 0;
+  }
+}
+
+enum DefaultEnum {
+  DEFAULT_ZERO = 0;
+}
+
+export enum ExportEnum {
+  EXPORT_ZERO = 0;
+}
+
+local enum LocalEnum {
+  LOCAL_ZERO = 0;
+}
diff --git a/cmd/protoc-gen-go/visibility_test.go b/cmd/protoc-gen-go/visibility_test.go
new file mode 100644
index 0000000..b3adf76
--- /dev/null
+++ b/cmd/protoc-gen-go/visibility_test.go
@@ -0,0 +1,115 @@
+// 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 (
+	"slices"
+	"strings"
+	"testing"
+
+	visibilitypb "google.golang.org/protobuf/cmd/protoc-gen-go/testdata/visibility"
+	"google.golang.org/protobuf/reflect/protodesc"
+	"google.golang.org/protobuf/types/descriptorpb"
+)
+
+func TestVisibility(t *testing.T) {
+	// Verify that the visibility modifiers aren't lost when converting to descriptor protos.
+	fd := visibilitypb.File_cmd_protoc_gen_go_testdata_visibility_visibility_proto
+	fdProto := protodesc.ToFileDescriptorProto(fd)
+
+	type testCase struct {
+		name       string
+		visibility descriptorpb.SymbolVisibility
+	}
+	testCases := []testCase{
+		{
+			name:       "DefaultMessage",
+			visibility: descriptorpb.SymbolVisibility_VISIBILITY_UNSET,
+		},
+		{
+			name:       "ExportMessage",
+			visibility: descriptorpb.SymbolVisibility_VISIBILITY_EXPORT,
+		},
+		{
+			name:       "LocalMessage",
+			visibility: descriptorpb.SymbolVisibility_VISIBILITY_LOCAL,
+		},
+		{
+			name:       "DefaultEnum",
+			visibility: descriptorpb.SymbolVisibility_VISIBILITY_UNSET,
+		},
+		{
+			name:       "ExportEnum",
+			visibility: descriptorpb.SymbolVisibility_VISIBILITY_EXPORT,
+		},
+		{
+			name:       "LocalEnum",
+			visibility: descriptorpb.SymbolVisibility_VISIBILITY_LOCAL,
+		},
+	}
+	// Instead of the boilerplate of handwritten cases for the nested types, we generate them
+	// algorithmically:
+	baseTestCases := slices.Clone(testCases)
+	for _, topLevelCase := range baseTestCases {
+		if strings.HasSuffix(topLevelCase.name, "Enum") {
+			continue // only messages have nested types
+		}
+		for _, nestedCase := range baseTestCases {
+			nestedCase.name = topLevelCase.name + ".Nested" + nestedCase.name
+			testCases = append(testCases, nestedCase)
+		}
+	}
+
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			path := strings.Split(tc.name, ".")
+			decl, visPtr := findByName(fdProto.MessageType, fdProto.EnumType, path)
+
+			if decl.GetVisibility() != tc.visibility {
+				t.Errorf("expected %v to have %v visibility but instead got %v", decl.GetName(), tc.visibility, decl.GetVisibility())
+			}
+			if tc.visibility == descriptorpb.SymbolVisibility_VISIBILITY_UNSET {
+				// For this one, we go one step further and expect the raw field to be a nil pointer.
+				if visPtr != nil {
+					t.Errorf("expected %v to have no visibility but instead got %v", decl.GetName(), visPtr)
+				}
+			}
+		})
+	}
+}
+
+func findByName(msgs []*descriptorpb.DescriptorProto, enums []*descriptorpb.EnumDescriptorProto, path []string) (hasVisibility, *descriptorpb.SymbolVisibility) {
+	name := path[0]
+	if len(path) > 1 {
+		// Looking for nested type.
+		for _, msg := range msgs {
+			if msg.GetName() == name {
+				return findByName(msg.NestedType, msg.EnumType, path[1:])
+			}
+		}
+		return nil, nil
+	}
+
+	if strings.HasSuffix(name, "Enum") {
+		for _, enum := range enums {
+			if enum.GetName() == name {
+				return enum, enum.Visibility
+			}
+		}
+		return nil, nil
+	}
+
+	for _, msg := range msgs {
+		if msg.GetName() == name {
+			return msg, msg.Visibility
+		}
+	}
+	return nil, nil
+}
+
+type hasVisibility interface {
+	GetName() string
+	GetVisibility() descriptorpb.SymbolVisibility
+}
diff --git a/internal/filedesc/desc.go b/internal/filedesc/desc.go
index 688aabe..dbcf90b 100644
--- a/internal/filedesc/desc.go
+++ b/internal/filedesc/desc.go
@@ -72,9 +72,10 @@
 		EditionFeatures EditionFeatures
 	}
 	FileL2 struct {
-		Options   func() protoreflect.ProtoMessage
-		Imports   FileImports
-		Locations SourceLocations
+		Options       func() protoreflect.ProtoMessage
+		Imports       FileImports
+		OptionImports func() protoreflect.FileImports
+		Locations     SourceLocations
 	}
 
 	// EditionFeatures is a frequently-instantiated struct, so please take care
@@ -126,12 +127,9 @@
 func (fd *File) Parent() protoreflect.Descriptor         { return nil }
 func (fd *File) Index() int                              { return 0 }
 func (fd *File) Syntax() protoreflect.Syntax             { return fd.L1.Syntax }
-
-// Not exported and just used to reconstruct the original FileDescriptor proto
-func (fd *File) Edition() int32                  { return int32(fd.L1.Edition) }
-func (fd *File) Name() protoreflect.Name         { return fd.L1.Package.Name() }
-func (fd *File) FullName() protoreflect.FullName { return fd.L1.Package }
-func (fd *File) IsPlaceholder() bool             { return false }
+func (fd *File) Name() protoreflect.Name                 { return fd.L1.Package.Name() }
+func (fd *File) FullName() protoreflect.FullName         { return fd.L1.Package }
+func (fd *File) IsPlaceholder() bool                     { return false }
 func (fd *File) Options() protoreflect.ProtoMessage {
 	if f := fd.lazyInit().Options; f != nil {
 		return f()
@@ -150,6 +148,16 @@
 func (fd *File) ProtoType(protoreflect.FileDescriptor)         {}
 func (fd *File) ProtoInternal(pragma.DoNotImplement)           {}
 
+// The next two are not part of the FileDescriptor interface. They are just used to reconstruct
+// the original FileDescriptor proto.
+func (fd *File) Edition() int32 { return int32(fd.L1.Edition) }
+func (fd *File) OptionImports() protoreflect.FileImports {
+	if f := fd.lazyInit().OptionImports; f != nil {
+		return f()
+	}
+	return emptyFiles
+}
+
 func (fd *File) lazyInit() *FileL2 {
 	if atomic.LoadUint32(&fd.once) == 0 {
 		fd.lazyInitOnce()
@@ -182,9 +190,9 @@
 		L2 *EnumL2 // protected by fileDesc.once
 	}
 	EnumL1 struct {
-		eagerValues bool // controls whether EnumL2.Values is already populated
-
 		EditionFeatures EditionFeatures
+		Visibility      int32
+		eagerValues     bool // controls whether EnumL2.Values is already populated
 	}
 	EnumL2 struct {
 		Options        func() protoreflect.ProtoMessage
@@ -219,6 +227,11 @@
 func (ed *Enum) ReservedRanges() protoreflect.EnumRanges { return &ed.lazyInit().ReservedRanges }
 func (ed *Enum) Format(s fmt.State, r rune)              { descfmt.FormatDesc(s, r, ed) }
 func (ed *Enum) ProtoType(protoreflect.EnumDescriptor)   {}
+
+// This is not part of the EnumDescriptor interface. It is just used to reconstruct
+// the original FileDescriptor proto.
+func (ed *Enum) Visibility() int32 { return ed.L1.Visibility }
+
 func (ed *Enum) lazyInit() *EnumL2 {
 	ed.L0.ParentFile.lazyInit() // implicitly initializes L2
 	return ed.L2
@@ -244,13 +257,13 @@
 		L2 *MessageL2 // protected by fileDesc.once
 	}
 	MessageL1 struct {
-		Enums        Enums
-		Messages     Messages
-		Extensions   Extensions
-		IsMapEntry   bool // promoted from google.protobuf.MessageOptions
-		IsMessageSet bool // promoted from google.protobuf.MessageOptions
-
+		Enums           Enums
+		Messages        Messages
+		Extensions      Extensions
 		EditionFeatures EditionFeatures
+		Visibility      int32
+		IsMapEntry      bool // promoted from google.protobuf.MessageOptions
+		IsMessageSet    bool // promoted from google.protobuf.MessageOptions
 	}
 	MessageL2 struct {
 		Options               func() protoreflect.ProtoMessage
@@ -319,6 +332,11 @@
 func (md *Message) Extensions() protoreflect.ExtensionDescriptors { return &md.L1.Extensions }
 func (md *Message) ProtoType(protoreflect.MessageDescriptor)      {}
 func (md *Message) Format(s fmt.State, r rune)                    { descfmt.FormatDesc(s, r, md) }
+
+// This is not part of the MessageDescriptor interface. It is just used to reconstruct
+// the original FileDescriptor proto.
+func (md *Message) Visibility() int32 { return md.L1.Visibility }
+
 func (md *Message) lazyInit() *MessageL2 {
 	md.L0.ParentFile.lazyInit() // implicitly initializes L2
 	return md.L2
diff --git a/internal/filedesc/desc_init.go b/internal/filedesc/desc_init.go
index d2f5494..e91860f 100644
--- a/internal/filedesc/desc_init.go
+++ b/internal/filedesc/desc_init.go
@@ -284,6 +284,13 @@
 			case genid.EnumDescriptorProto_Value_field_number:
 				numValues++
 			}
+		case protowire.VarintType:
+			v, m := protowire.ConsumeVarint(b)
+			b = b[m:]
+			switch num {
+			case genid.EnumDescriptorProto_Visibility_field_number:
+				ed.L1.Visibility = int32(v)
+			}
 		default:
 			m := protowire.ConsumeFieldValue(num, typ, b)
 			b = b[m:]
@@ -365,6 +372,13 @@
 				md.unmarshalSeedOptions(v)
 			}
 			prevField = num
+		case protowire.VarintType:
+			v, m := protowire.ConsumeVarint(b)
+			b = b[m:]
+			switch num {
+			case genid.DescriptorProto_Visibility_field_number:
+				md.L1.Visibility = int32(v)
+			}
 		default:
 			m := protowire.ConsumeFieldValue(num, typ, b)
 			b = b[m:]
diff --git a/internal/filedesc/desc_lazy.go b/internal/filedesc/desc_lazy.go
index d4c9445..dd31faa 100644
--- a/internal/filedesc/desc_lazy.go
+++ b/internal/filedesc/desc_lazy.go
@@ -134,6 +134,7 @@
 
 	var enumIdx, messageIdx, extensionIdx, serviceIdx int
 	var rawOptions []byte
+	var optionImports []string
 	fd.L2 = new(FileL2)
 	for len(b) > 0 {
 		num, typ, n := protowire.ConsumeTag(b)
@@ -157,6 +158,8 @@
 					imp = PlaceholderFile(path)
 				}
 				fd.L2.Imports = append(fd.L2.Imports, protoreflect.FileImport{FileDescriptor: imp})
+			case genid.FileDescriptorProto_OptionDependency_field_number:
+				optionImports = append(optionImports, sb.MakeString(v))
 			case genid.FileDescriptorProto_EnumType_field_number:
 				fd.L1.Enums.List[enumIdx].unmarshalFull(v, sb)
 				enumIdx++
@@ -178,6 +181,23 @@
 		}
 	}
 	fd.L2.Options = fd.builder.optionsUnmarshaler(&descopts.File, rawOptions)
+	if len(optionImports) > 0 {
+		var imps FileImports
+		var once sync.Once
+		fd.L2.OptionImports = func() protoreflect.FileImports {
+			once.Do(func() {
+				imps = make(FileImports, len(optionImports))
+				for i, path := range optionImports {
+					imp, _ := fd.builder.FileRegistry.FindFileByPath(path)
+					if imp == nil {
+						imp = PlaceholderFile(path)
+					}
+					imps[i] = protoreflect.FileImport{FileDescriptor: imp}
+				}
+			})
+			return &imps
+		}
+	}
 }
 
 func (ed *Enum) unmarshalFull(b []byte, sb *strs.Builder) {
diff --git a/reflect/protodesc/desc.go b/reflect/protodesc/desc.go
index 823dbf3..9196288 100644
--- a/reflect/protodesc/desc.go
+++ b/reflect/protodesc/desc.go
@@ -152,6 +152,28 @@
 		imp := &f.L2.Imports[i]
 		imps.importPublic(imp.Imports())
 	}
+	if len(fd.GetOptionDependency()) > 0 {
+		optionImports := make(filedesc.FileImports, len(fd.GetOptionDependency()))
+		for i, path := range fd.GetOptionDependency() {
+			imp := &optionImports[i]
+			f, err := r.FindFileByPath(path)
+			if err == protoregistry.NotFound {
+				// We always allow option imports to be unresolvable.
+				f = filedesc.PlaceholderFile(path)
+			} else if err != nil {
+				return nil, errors.New("could not resolve import %q: %v", path, err)
+			}
+			imp.FileDescriptor = f
+
+			if imps[imp.Path()] {
+				return nil, errors.New("already imported %q", path)
+			}
+			imps[imp.Path()] = true
+		}
+		f.L2.OptionImports = func() protoreflect.FileImports {
+			return &optionImports
+		}
+	}
 
 	// Handle source locations.
 	f.L2.Locations.File = f
diff --git a/reflect/protodesc/desc_init.go b/reflect/protodesc/desc_init.go
index 9da3499..c826ad0 100644
--- a/reflect/protodesc/desc_init.go
+++ b/reflect/protodesc/desc_init.go
@@ -29,6 +29,7 @@
 			e.L2.Options = func() protoreflect.ProtoMessage { return opts }
 		}
 		e.L1.EditionFeatures = mergeEditionFeatures(parent, ed.GetOptions().GetFeatures())
+		e.L1.Visibility = int32(ed.GetVisibility())
 		for _, s := range ed.GetReservedName() {
 			e.L2.ReservedNames.List = append(e.L2.ReservedNames.List, protoreflect.Name(s))
 		}
@@ -70,6 +71,7 @@
 			return nil, err
 		}
 		m.L1.EditionFeatures = mergeEditionFeatures(parent, md.GetOptions().GetFeatures())
+		m.L1.Visibility = int32(md.GetVisibility())
 		if opts := md.GetOptions(); opts != nil {
 			opts = proto.Clone(opts).(*descriptorpb.MessageOptions)
 			m.L2.Options = func() protoreflect.ProtoMessage { return opts }
diff --git a/reflect/protodesc/proto.go b/reflect/protodesc/proto.go
index 9b880aa..6f91074 100644
--- a/reflect/protodesc/proto.go
+++ b/reflect/protodesc/proto.go
@@ -70,16 +70,27 @@
 	if syntax := file.Syntax(); syntax != protoreflect.Proto2 && syntax.IsValid() {
 		p.Syntax = proto.String(file.Syntax().String())
 	}
+	desc := file
+	if fileImportDesc, ok := file.(protoreflect.FileImport); ok {
+		desc = fileImportDesc.FileDescriptor
+	}
 	if file.Syntax() == protoreflect.Editions {
-		desc := file
-		if fileImportDesc, ok := file.(protoreflect.FileImport); ok {
-			desc = fileImportDesc.FileDescriptor
-		}
-
 		if editionsInterface, ok := desc.(interface{ Edition() int32 }); ok {
 			p.Edition = descriptorpb.Edition(editionsInterface.Edition()).Enum()
 		}
 	}
+	type hasOptionImports interface {
+		OptionImports() protoreflect.FileImports
+	}
+	if opts, ok := desc.(hasOptionImports); ok {
+		if optionImports := opts.OptionImports(); optionImports.Len() > 0 {
+			optionDeps := make([]string, optionImports.Len())
+			for i := range optionImports.Len() {
+				optionDeps[i] = optionImports.Get(i).Path()
+			}
+			p.OptionDependency = optionDeps
+		}
+	}
 	return p
 }
 
@@ -123,6 +134,14 @@
 	for i, names := 0, message.ReservedNames(); i < names.Len(); i++ {
 		p.ReservedName = append(p.ReservedName, string(names.Get(i)))
 	}
+	type hasVisibility interface {
+		Visibility() int32
+	}
+	if vis, ok := message.(hasVisibility); ok {
+		if visibility := vis.Visibility(); visibility > 0 {
+			p.Visibility = descriptorpb.SymbolVisibility(visibility).Enum()
+		}
+	}
 	return p
 }
 
@@ -216,6 +235,14 @@
 	for i, names := 0, enum.ReservedNames(); i < names.Len(); i++ {
 		p.ReservedName = append(p.ReservedName, string(names.Get(i)))
 	}
+	type hasVisibility interface {
+		Visibility() int32
+	}
+	if vis, ok := enum.(hasVisibility); ok {
+		if visibility := vis.Visibility(); visibility > 0 {
+			p.Visibility = descriptorpb.SymbolVisibility(visibility).Enum()
+		}
+	}
 	return p
 }
 
diff --git a/reflect/protodesc/proto_test.go b/reflect/protodesc/proto_test.go
index ec09123..f4a169c 100644
--- a/reflect/protodesc/proto_test.go
+++ b/reflect/protodesc/proto_test.go
@@ -11,8 +11,10 @@
 	"google.golang.org/protobuf/internal/filedesc"
 	"google.golang.org/protobuf/proto"
 	"google.golang.org/protobuf/reflect/protoreflect"
+	"google.golang.org/protobuf/reflect/protoregistry"
 	"google.golang.org/protobuf/testing/protocmp"
 	"google.golang.org/protobuf/types/descriptorpb"
+	"google.golang.org/protobuf/types/dynamicpb"
 )
 
 func TestEditionsRequired(t *testing.T) {
@@ -108,3 +110,126 @@
 		t.Errorf("ToFieldDescriptor: unexpected diff (-want +got):\n%s", diff)
 	}
 }
+
+func TestEdition2024Attributes(t *testing.T) {
+	// Edition 2024 introduces new language capabilities which are used to populate new
+	// attributes in a FileDescriptorProto. But none of them are used by the runtime so
+	// are not accessible via protoreflect.FileDescriptor. This verifies that, despite
+	// there not being a way to access them through a protoreflect.FileDescriptor, they
+	// still successfully make it through a round-trip of descriptor proto -> descriptor
+	// and back.
+	optionsProto := &descriptorpb.FileDescriptorProto{
+		Name:       proto.String("foo_options.proto"),
+		Package:    proto.String("foo"),
+		Syntax:     proto.String("proto3"),
+		Dependency: []string{"google/protobuf/descriptor.proto"},
+		Extension: []*descriptorpb.FieldDescriptorProto{
+			{
+				Extendee: proto.String("google.protobuf.FileOptions"),
+				Name:     proto.String("foo_option"),
+				Type:     descriptorpb.FieldDescriptorProto_TYPE_STRING.Enum(),
+				Label:    descriptorpb.FieldDescriptorProto_LABEL_OPTIONAL.Enum(),
+				Number:   proto.Int32(10101),
+			},
+		},
+	}
+
+	fd, err := NewFile(optionsProto, protoregistry.GlobalFiles)
+	if err != nil {
+		t.Fatalf("failed to create protoreflect.FileDescriptor: %v", err)
+	}
+	fooFileOption := dynamicpb.NewExtensionType(fd.Extensions().Get(0))
+	fileOpts := &descriptorpb.FileOptions{}
+	proto.SetExtension(fileOpts, fooFileOption, "abc")
+
+	msgs := []*descriptorpb.DescriptorProto{
+		{
+			Name: proto.String("DefaultMessage"),
+		},
+		{
+			Name:       proto.String("ExportMessage"),
+			Visibility: descriptorpb.SymbolVisibility_VISIBILITY_EXPORT.Enum(),
+		},
+		{
+			Name:       proto.String("LocalMessage"),
+			Visibility: descriptorpb.SymbolVisibility_VISIBILITY_LOCAL.Enum(),
+		},
+	}
+	enums := []*descriptorpb.EnumDescriptorProto{
+		{
+			Name: proto.String("DefaultEnum"),
+			Value: []*descriptorpb.EnumValueDescriptorProto{
+				{
+					Name:   proto.String("DEFAULT_ZERO"),
+					Number: proto.Int32(0),
+				},
+			},
+		},
+		{
+			Name:       proto.String("ExportEnum"),
+			Visibility: descriptorpb.SymbolVisibility_VISIBILITY_EXPORT.Enum(),
+			Value: []*descriptorpb.EnumValueDescriptorProto{
+				{
+					Name:   proto.String("EXPORT_ZERO"),
+					Number: proto.Int32(0),
+				},
+			},
+		},
+		{
+			Name:       proto.String("LocalEnum"),
+			Visibility: descriptorpb.SymbolVisibility_VISIBILITY_LOCAL.Enum(),
+			Value: []*descriptorpb.EnumValueDescriptorProto{
+				{
+					Name:   proto.String("LOCAL_ZERO"),
+					Number: proto.Int32(0),
+				},
+			},
+		},
+	}
+
+	fdProto := &descriptorpb.FileDescriptorProto{
+		Name:             proto.String("foo.proto"),
+		Package:          proto.String("foo"),
+		Syntax:           proto.String("editions"),
+		Edition:          descriptorpb.Edition_EDITION_2024.Enum(),
+		OptionDependency: []string{"foo_options.proto"},
+		Options:          fileOpts,
+		MessageType: []*descriptorpb.DescriptorProto{
+			{
+				Name:       proto.String("DefaultMessage"),
+				NestedType: msgs,
+				EnumType:   enums,
+			},
+			{
+				Name:       proto.String("ExportMessage"),
+				Visibility: descriptorpb.SymbolVisibility_VISIBILITY_EXPORT.Enum(),
+				NestedType: msgs,
+				EnumType:   enums,
+			},
+			{
+				Name:       proto.String("LocalMessage"),
+				Visibility: descriptorpb.SymbolVisibility_VISIBILITY_LOCAL.Enum(),
+				NestedType: msgs,
+				EnumType:   enums,
+			},
+		},
+		EnumType: enums,
+	}
+
+	var reg protoregistry.Files
+	if err := reg.RegisterFile(descriptorpb.File_google_protobuf_descriptor_proto); err != nil {
+		t.Fatalf("failed to register google/protobuf/descriptor.proto: %v", err)
+	}
+	if err := reg.RegisterFile(fd); err != nil {
+		t.Fatalf("failed to register foo_options.proto: %v", err)
+	}
+	fd, err = NewFile(fdProto, &reg)
+	if err != nil {
+		t.Fatalf("failed to create protoreflect.FileDescriptor: %v", err)
+	}
+
+	roundTripped := ToFileDescriptorProto(fd)
+	if diff := cmp.Diff(fdProto, roundTripped, protocmp.Transform()); diff != "" {
+		t.Fatalf("file did not survive round trip unchanged; diff:\n%s", diff)
+	}
+}