xref: /aosp_15_r20/external/golang-protobuf/cmd/protoc-gen-go/internal_gengo/main.go (revision 1c12ee1efe575feb122dbf939ff15148a3b3e8f2)
1*1c12ee1eSDan Willemsen// Copyright 2018 The Go Authors. All rights reserved.
2*1c12ee1eSDan Willemsen// Use of this source code is governed by a BSD-style
3*1c12ee1eSDan Willemsen// license that can be found in the LICENSE file.
4*1c12ee1eSDan Willemsen
5*1c12ee1eSDan Willemsen// Package internal_gengo is internal to the protobuf module.
6*1c12ee1eSDan Willemsenpackage internal_gengo
7*1c12ee1eSDan Willemsen
8*1c12ee1eSDan Willemsenimport (
9*1c12ee1eSDan Willemsen	"fmt"
10*1c12ee1eSDan Willemsen	"go/ast"
11*1c12ee1eSDan Willemsen	"go/parser"
12*1c12ee1eSDan Willemsen	"go/token"
13*1c12ee1eSDan Willemsen	"math"
14*1c12ee1eSDan Willemsen	"strconv"
15*1c12ee1eSDan Willemsen	"strings"
16*1c12ee1eSDan Willemsen	"unicode"
17*1c12ee1eSDan Willemsen	"unicode/utf8"
18*1c12ee1eSDan Willemsen
19*1c12ee1eSDan Willemsen	"google.golang.org/protobuf/compiler/protogen"
20*1c12ee1eSDan Willemsen	"google.golang.org/protobuf/internal/encoding/tag"
21*1c12ee1eSDan Willemsen	"google.golang.org/protobuf/internal/genid"
22*1c12ee1eSDan Willemsen	"google.golang.org/protobuf/internal/version"
23*1c12ee1eSDan Willemsen	"google.golang.org/protobuf/reflect/protoreflect"
24*1c12ee1eSDan Willemsen	"google.golang.org/protobuf/runtime/protoimpl"
25*1c12ee1eSDan Willemsen
26*1c12ee1eSDan Willemsen	"google.golang.org/protobuf/types/descriptorpb"
27*1c12ee1eSDan Willemsen	"google.golang.org/protobuf/types/pluginpb"
28*1c12ee1eSDan Willemsen)
29*1c12ee1eSDan Willemsen
30*1c12ee1eSDan Willemsen// SupportedFeatures reports the set of supported protobuf language features.
31*1c12ee1eSDan Willemsenvar SupportedFeatures = uint64(pluginpb.CodeGeneratorResponse_FEATURE_PROTO3_OPTIONAL)
32*1c12ee1eSDan Willemsen
33*1c12ee1eSDan Willemsen// GenerateVersionMarkers specifies whether to generate version markers.
34*1c12ee1eSDan Willemsenvar GenerateVersionMarkers = true
35*1c12ee1eSDan Willemsen
36*1c12ee1eSDan Willemsen// Standard library dependencies.
37*1c12ee1eSDan Willemsenconst (
38*1c12ee1eSDan Willemsen	base64Package  = protogen.GoImportPath("encoding/base64")
39*1c12ee1eSDan Willemsen	mathPackage    = protogen.GoImportPath("math")
40*1c12ee1eSDan Willemsen	reflectPackage = protogen.GoImportPath("reflect")
41*1c12ee1eSDan Willemsen	sortPackage    = protogen.GoImportPath("sort")
42*1c12ee1eSDan Willemsen	stringsPackage = protogen.GoImportPath("strings")
43*1c12ee1eSDan Willemsen	syncPackage    = protogen.GoImportPath("sync")
44*1c12ee1eSDan Willemsen	timePackage    = protogen.GoImportPath("time")
45*1c12ee1eSDan Willemsen	utf8Package    = protogen.GoImportPath("unicode/utf8")
46*1c12ee1eSDan Willemsen)
47*1c12ee1eSDan Willemsen
48*1c12ee1eSDan Willemsen// Protobuf library dependencies.
49*1c12ee1eSDan Willemsen//
50*1c12ee1eSDan Willemsen// These are declared as an interface type so that they can be more easily
51*1c12ee1eSDan Willemsen// patched to support unique build environments that impose restrictions
52*1c12ee1eSDan Willemsen// on the dependencies of generated source code.
53*1c12ee1eSDan Willemsenvar (
54*1c12ee1eSDan Willemsen	protoPackage         goImportPath = protogen.GoImportPath("google.golang.org/protobuf/proto")
55*1c12ee1eSDan Willemsen	protoifacePackage    goImportPath = protogen.GoImportPath("google.golang.org/protobuf/runtime/protoiface")
56*1c12ee1eSDan Willemsen	protoimplPackage     goImportPath = protogen.GoImportPath("google.golang.org/protobuf/runtime/protoimpl")
57*1c12ee1eSDan Willemsen	protojsonPackage     goImportPath = protogen.GoImportPath("google.golang.org/protobuf/encoding/protojson")
58*1c12ee1eSDan Willemsen	protoreflectPackage  goImportPath = protogen.GoImportPath("google.golang.org/protobuf/reflect/protoreflect")
59*1c12ee1eSDan Willemsen	protoregistryPackage goImportPath = protogen.GoImportPath("google.golang.org/protobuf/reflect/protoregistry")
60*1c12ee1eSDan Willemsen)
61*1c12ee1eSDan Willemsen
62*1c12ee1eSDan Willemsentype goImportPath interface {
63*1c12ee1eSDan Willemsen	String() string
64*1c12ee1eSDan Willemsen	Ident(string) protogen.GoIdent
65*1c12ee1eSDan Willemsen}
66*1c12ee1eSDan Willemsen
67*1c12ee1eSDan Willemsen// GenerateFile generates the contents of a .pb.go file.
68*1c12ee1eSDan Willemsenfunc GenerateFile(gen *protogen.Plugin, file *protogen.File) *protogen.GeneratedFile {
69*1c12ee1eSDan Willemsen	filename := file.GeneratedFilenamePrefix + ".pb.go"
70*1c12ee1eSDan Willemsen	g := gen.NewGeneratedFile(filename, file.GoImportPath)
71*1c12ee1eSDan Willemsen	f := newFileInfo(file)
72*1c12ee1eSDan Willemsen
73*1c12ee1eSDan Willemsen	genStandaloneComments(g, f, int32(genid.FileDescriptorProto_Syntax_field_number))
74*1c12ee1eSDan Willemsen	genGeneratedHeader(gen, g, f)
75*1c12ee1eSDan Willemsen	genStandaloneComments(g, f, int32(genid.FileDescriptorProto_Package_field_number))
76*1c12ee1eSDan Willemsen
77*1c12ee1eSDan Willemsen	packageDoc := genPackageKnownComment(f)
78*1c12ee1eSDan Willemsen	g.P(packageDoc, "package ", f.GoPackageName)
79*1c12ee1eSDan Willemsen	g.P()
80*1c12ee1eSDan Willemsen
81*1c12ee1eSDan Willemsen	// Emit a static check that enforces a minimum version of the proto package.
82*1c12ee1eSDan Willemsen	if GenerateVersionMarkers {
83*1c12ee1eSDan Willemsen		g.P("const (")
84*1c12ee1eSDan Willemsen		g.P("// Verify that this generated code is sufficiently up-to-date.")
85*1c12ee1eSDan Willemsen		g.P("_ = ", protoimplPackage.Ident("EnforceVersion"), "(", protoimpl.GenVersion, " - ", protoimplPackage.Ident("MinVersion"), ")")
86*1c12ee1eSDan Willemsen		g.P("// Verify that runtime/protoimpl is sufficiently up-to-date.")
87*1c12ee1eSDan Willemsen		g.P("_ = ", protoimplPackage.Ident("EnforceVersion"), "(", protoimplPackage.Ident("MaxVersion"), " - ", protoimpl.GenVersion, ")")
88*1c12ee1eSDan Willemsen		g.P(")")
89*1c12ee1eSDan Willemsen		g.P()
90*1c12ee1eSDan Willemsen	}
91*1c12ee1eSDan Willemsen
92*1c12ee1eSDan Willemsen	for i, imps := 0, f.Desc.Imports(); i < imps.Len(); i++ {
93*1c12ee1eSDan Willemsen		genImport(gen, g, f, imps.Get(i))
94*1c12ee1eSDan Willemsen	}
95*1c12ee1eSDan Willemsen	for _, enum := range f.allEnums {
96*1c12ee1eSDan Willemsen		genEnum(g, f, enum)
97*1c12ee1eSDan Willemsen	}
98*1c12ee1eSDan Willemsen	for _, message := range f.allMessages {
99*1c12ee1eSDan Willemsen		genMessage(g, f, message)
100*1c12ee1eSDan Willemsen	}
101*1c12ee1eSDan Willemsen	genExtensions(g, f)
102*1c12ee1eSDan Willemsen
103*1c12ee1eSDan Willemsen	genReflectFileDescriptor(gen, g, f)
104*1c12ee1eSDan Willemsen
105*1c12ee1eSDan Willemsen	return g
106*1c12ee1eSDan Willemsen}
107*1c12ee1eSDan Willemsen
108*1c12ee1eSDan Willemsen// genStandaloneComments prints all leading comments for a FileDescriptorProto
109*1c12ee1eSDan Willemsen// location identified by the field number n.
110*1c12ee1eSDan Willemsenfunc genStandaloneComments(g *protogen.GeneratedFile, f *fileInfo, n int32) {
111*1c12ee1eSDan Willemsen	loc := f.Desc.SourceLocations().ByPath(protoreflect.SourcePath{n})
112*1c12ee1eSDan Willemsen	for _, s := range loc.LeadingDetachedComments {
113*1c12ee1eSDan Willemsen		g.P(protogen.Comments(s))
114*1c12ee1eSDan Willemsen		g.P()
115*1c12ee1eSDan Willemsen	}
116*1c12ee1eSDan Willemsen	if s := loc.LeadingComments; s != "" {
117*1c12ee1eSDan Willemsen		g.P(protogen.Comments(s))
118*1c12ee1eSDan Willemsen		g.P()
119*1c12ee1eSDan Willemsen	}
120*1c12ee1eSDan Willemsen}
121*1c12ee1eSDan Willemsen
122*1c12ee1eSDan Willemsenfunc genGeneratedHeader(gen *protogen.Plugin, g *protogen.GeneratedFile, f *fileInfo) {
123*1c12ee1eSDan Willemsen	g.P("// Code generated by protoc-gen-go. DO NOT EDIT.")
124*1c12ee1eSDan Willemsen
125*1c12ee1eSDan Willemsen	if GenerateVersionMarkers {
126*1c12ee1eSDan Willemsen		g.P("// versions:")
127*1c12ee1eSDan Willemsen		protocGenGoVersion := version.String()
128*1c12ee1eSDan Willemsen		protocVersion := "(unknown)"
129*1c12ee1eSDan Willemsen		if v := gen.Request.GetCompilerVersion(); v != nil {
130*1c12ee1eSDan Willemsen			protocVersion = fmt.Sprintf("v%v.%v.%v", v.GetMajor(), v.GetMinor(), v.GetPatch())
131*1c12ee1eSDan Willemsen			if s := v.GetSuffix(); s != "" {
132*1c12ee1eSDan Willemsen				protocVersion += "-" + s
133*1c12ee1eSDan Willemsen			}
134*1c12ee1eSDan Willemsen		}
135*1c12ee1eSDan Willemsen		g.P("// \tprotoc-gen-go ", protocGenGoVersion)
136*1c12ee1eSDan Willemsen		g.P("// \tprotoc        ", protocVersion)
137*1c12ee1eSDan Willemsen	}
138*1c12ee1eSDan Willemsen
139*1c12ee1eSDan Willemsen	if f.Proto.GetOptions().GetDeprecated() {
140*1c12ee1eSDan Willemsen		g.P("// ", f.Desc.Path(), " is a deprecated file.")
141*1c12ee1eSDan Willemsen	} else {
142*1c12ee1eSDan Willemsen		g.P("// source: ", f.Desc.Path())
143*1c12ee1eSDan Willemsen	}
144*1c12ee1eSDan Willemsen	g.P()
145*1c12ee1eSDan Willemsen}
146*1c12ee1eSDan Willemsen
147*1c12ee1eSDan Willemsenfunc genImport(gen *protogen.Plugin, g *protogen.GeneratedFile, f *fileInfo, imp protoreflect.FileImport) {
148*1c12ee1eSDan Willemsen	impFile, ok := gen.FilesByPath[imp.Path()]
149*1c12ee1eSDan Willemsen	if !ok {
150*1c12ee1eSDan Willemsen		return
151*1c12ee1eSDan Willemsen	}
152*1c12ee1eSDan Willemsen	if impFile.GoImportPath == f.GoImportPath {
153*1c12ee1eSDan Willemsen		// Don't generate imports or aliases for types in the same Go package.
154*1c12ee1eSDan Willemsen		return
155*1c12ee1eSDan Willemsen	}
156*1c12ee1eSDan Willemsen	// Generate imports for all non-weak dependencies, even if they are not
157*1c12ee1eSDan Willemsen	// referenced, because other code and tools depend on having the
158*1c12ee1eSDan Willemsen	// full transitive closure of protocol buffer types in the binary.
159*1c12ee1eSDan Willemsen	if !imp.IsWeak {
160*1c12ee1eSDan Willemsen		g.Import(impFile.GoImportPath)
161*1c12ee1eSDan Willemsen	}
162*1c12ee1eSDan Willemsen	if !imp.IsPublic {
163*1c12ee1eSDan Willemsen		return
164*1c12ee1eSDan Willemsen	}
165*1c12ee1eSDan Willemsen
166*1c12ee1eSDan Willemsen	// Generate public imports by generating the imported file, parsing it,
167*1c12ee1eSDan Willemsen	// and extracting every symbol that should receive a forwarding declaration.
168*1c12ee1eSDan Willemsen	impGen := GenerateFile(gen, impFile)
169*1c12ee1eSDan Willemsen	impGen.Skip()
170*1c12ee1eSDan Willemsen	b, err := impGen.Content()
171*1c12ee1eSDan Willemsen	if err != nil {
172*1c12ee1eSDan Willemsen		gen.Error(err)
173*1c12ee1eSDan Willemsen		return
174*1c12ee1eSDan Willemsen	}
175*1c12ee1eSDan Willemsen	fset := token.NewFileSet()
176*1c12ee1eSDan Willemsen	astFile, err := parser.ParseFile(fset, "", b, parser.ParseComments)
177*1c12ee1eSDan Willemsen	if err != nil {
178*1c12ee1eSDan Willemsen		gen.Error(err)
179*1c12ee1eSDan Willemsen		return
180*1c12ee1eSDan Willemsen	}
181*1c12ee1eSDan Willemsen	genForward := func(tok token.Token, name string, expr ast.Expr) {
182*1c12ee1eSDan Willemsen		// Don't import unexported symbols.
183*1c12ee1eSDan Willemsen		r, _ := utf8.DecodeRuneInString(name)
184*1c12ee1eSDan Willemsen		if !unicode.IsUpper(r) {
185*1c12ee1eSDan Willemsen			return
186*1c12ee1eSDan Willemsen		}
187*1c12ee1eSDan Willemsen		// Don't import the FileDescriptor.
188*1c12ee1eSDan Willemsen		if name == impFile.GoDescriptorIdent.GoName {
189*1c12ee1eSDan Willemsen			return
190*1c12ee1eSDan Willemsen		}
191*1c12ee1eSDan Willemsen		// Don't import decls referencing a symbol defined in another package.
192*1c12ee1eSDan Willemsen		// i.e., don't import decls which are themselves public imports:
193*1c12ee1eSDan Willemsen		//
194*1c12ee1eSDan Willemsen		//	type T = somepackage.T
195*1c12ee1eSDan Willemsen		if _, ok := expr.(*ast.SelectorExpr); ok {
196*1c12ee1eSDan Willemsen			return
197*1c12ee1eSDan Willemsen		}
198*1c12ee1eSDan Willemsen		g.P(tok, " ", name, " = ", impFile.GoImportPath.Ident(name))
199*1c12ee1eSDan Willemsen	}
200*1c12ee1eSDan Willemsen	g.P("// Symbols defined in public import of ", imp.Path(), ".")
201*1c12ee1eSDan Willemsen	g.P()
202*1c12ee1eSDan Willemsen	for _, decl := range astFile.Decls {
203*1c12ee1eSDan Willemsen		switch decl := decl.(type) {
204*1c12ee1eSDan Willemsen		case *ast.GenDecl:
205*1c12ee1eSDan Willemsen			for _, spec := range decl.Specs {
206*1c12ee1eSDan Willemsen				switch spec := spec.(type) {
207*1c12ee1eSDan Willemsen				case *ast.TypeSpec:
208*1c12ee1eSDan Willemsen					genForward(decl.Tok, spec.Name.Name, spec.Type)
209*1c12ee1eSDan Willemsen				case *ast.ValueSpec:
210*1c12ee1eSDan Willemsen					for i, name := range spec.Names {
211*1c12ee1eSDan Willemsen						var expr ast.Expr
212*1c12ee1eSDan Willemsen						if i < len(spec.Values) {
213*1c12ee1eSDan Willemsen							expr = spec.Values[i]
214*1c12ee1eSDan Willemsen						}
215*1c12ee1eSDan Willemsen						genForward(decl.Tok, name.Name, expr)
216*1c12ee1eSDan Willemsen					}
217*1c12ee1eSDan Willemsen				case *ast.ImportSpec:
218*1c12ee1eSDan Willemsen				default:
219*1c12ee1eSDan Willemsen					panic(fmt.Sprintf("can't generate forward for spec type %T", spec))
220*1c12ee1eSDan Willemsen				}
221*1c12ee1eSDan Willemsen			}
222*1c12ee1eSDan Willemsen		}
223*1c12ee1eSDan Willemsen	}
224*1c12ee1eSDan Willemsen	g.P()
225*1c12ee1eSDan Willemsen}
226*1c12ee1eSDan Willemsen
227*1c12ee1eSDan Willemsenfunc genEnum(g *protogen.GeneratedFile, f *fileInfo, e *enumInfo) {
228*1c12ee1eSDan Willemsen	// Enum type declaration.
229*1c12ee1eSDan Willemsen	g.Annotate(e.GoIdent.GoName, e.Location)
230*1c12ee1eSDan Willemsen	leadingComments := appendDeprecationSuffix(e.Comments.Leading,
231*1c12ee1eSDan Willemsen		e.Desc.ParentFile(),
232*1c12ee1eSDan Willemsen		e.Desc.Options().(*descriptorpb.EnumOptions).GetDeprecated())
233*1c12ee1eSDan Willemsen	g.P(leadingComments,
234*1c12ee1eSDan Willemsen		"type ", e.GoIdent, " int32")
235*1c12ee1eSDan Willemsen
236*1c12ee1eSDan Willemsen	// Enum value constants.
237*1c12ee1eSDan Willemsen	g.P("const (")
238*1c12ee1eSDan Willemsen	for _, value := range e.Values {
239*1c12ee1eSDan Willemsen		g.Annotate(value.GoIdent.GoName, value.Location)
240*1c12ee1eSDan Willemsen		leadingComments := appendDeprecationSuffix(value.Comments.Leading,
241*1c12ee1eSDan Willemsen			value.Desc.ParentFile(),
242*1c12ee1eSDan Willemsen			value.Desc.Options().(*descriptorpb.EnumValueOptions).GetDeprecated())
243*1c12ee1eSDan Willemsen		g.P(leadingComments,
244*1c12ee1eSDan Willemsen			value.GoIdent, " ", e.GoIdent, " = ", value.Desc.Number(),
245*1c12ee1eSDan Willemsen			trailingComment(value.Comments.Trailing))
246*1c12ee1eSDan Willemsen	}
247*1c12ee1eSDan Willemsen	g.P(")")
248*1c12ee1eSDan Willemsen	g.P()
249*1c12ee1eSDan Willemsen
250*1c12ee1eSDan Willemsen	// Enum value maps.
251*1c12ee1eSDan Willemsen	g.P("// Enum value maps for ", e.GoIdent, ".")
252*1c12ee1eSDan Willemsen	g.P("var (")
253*1c12ee1eSDan Willemsen	g.P(e.GoIdent.GoName+"_name", " = map[int32]string{")
254*1c12ee1eSDan Willemsen	for _, value := range e.Values {
255*1c12ee1eSDan Willemsen		duplicate := ""
256*1c12ee1eSDan Willemsen		if value.Desc != e.Desc.Values().ByNumber(value.Desc.Number()) {
257*1c12ee1eSDan Willemsen			duplicate = "// Duplicate value: "
258*1c12ee1eSDan Willemsen		}
259*1c12ee1eSDan Willemsen		g.P(duplicate, value.Desc.Number(), ": ", strconv.Quote(string(value.Desc.Name())), ",")
260*1c12ee1eSDan Willemsen	}
261*1c12ee1eSDan Willemsen	g.P("}")
262*1c12ee1eSDan Willemsen	g.P(e.GoIdent.GoName+"_value", " = map[string]int32{")
263*1c12ee1eSDan Willemsen	for _, value := range e.Values {
264*1c12ee1eSDan Willemsen		g.P(strconv.Quote(string(value.Desc.Name())), ": ", value.Desc.Number(), ",")
265*1c12ee1eSDan Willemsen	}
266*1c12ee1eSDan Willemsen	g.P("}")
267*1c12ee1eSDan Willemsen	g.P(")")
268*1c12ee1eSDan Willemsen	g.P()
269*1c12ee1eSDan Willemsen
270*1c12ee1eSDan Willemsen	// Enum method.
271*1c12ee1eSDan Willemsen	//
272*1c12ee1eSDan Willemsen	// NOTE: A pointer value is needed to represent presence in proto2.
273*1c12ee1eSDan Willemsen	// Since a proto2 message can reference a proto3 enum, it is useful to
274*1c12ee1eSDan Willemsen	// always generate this method (even on proto3 enums) to support that case.
275*1c12ee1eSDan Willemsen	g.P("func (x ", e.GoIdent, ") Enum() *", e.GoIdent, " {")
276*1c12ee1eSDan Willemsen	g.P("p := new(", e.GoIdent, ")")
277*1c12ee1eSDan Willemsen	g.P("*p = x")
278*1c12ee1eSDan Willemsen	g.P("return p")
279*1c12ee1eSDan Willemsen	g.P("}")
280*1c12ee1eSDan Willemsen	g.P()
281*1c12ee1eSDan Willemsen
282*1c12ee1eSDan Willemsen	// String method.
283*1c12ee1eSDan Willemsen	g.P("func (x ", e.GoIdent, ") String() string {")
284*1c12ee1eSDan Willemsen	g.P("return ", protoimplPackage.Ident("X"), ".EnumStringOf(x.Descriptor(), ", protoreflectPackage.Ident("EnumNumber"), "(x))")
285*1c12ee1eSDan Willemsen	g.P("}")
286*1c12ee1eSDan Willemsen	g.P()
287*1c12ee1eSDan Willemsen
288*1c12ee1eSDan Willemsen	genEnumReflectMethods(g, f, e)
289*1c12ee1eSDan Willemsen
290*1c12ee1eSDan Willemsen	// UnmarshalJSON method.
291*1c12ee1eSDan Willemsen	if e.genJSONMethod && e.Desc.Syntax() == protoreflect.Proto2 {
292*1c12ee1eSDan Willemsen		g.P("// Deprecated: Do not use.")
293*1c12ee1eSDan Willemsen		g.P("func (x *", e.GoIdent, ") UnmarshalJSON(b []byte) error {")
294*1c12ee1eSDan Willemsen		g.P("num, err := ", protoimplPackage.Ident("X"), ".UnmarshalJSONEnum(x.Descriptor(), b)")
295*1c12ee1eSDan Willemsen		g.P("if err != nil {")
296*1c12ee1eSDan Willemsen		g.P("return err")
297*1c12ee1eSDan Willemsen		g.P("}")
298*1c12ee1eSDan Willemsen		g.P("*x = ", e.GoIdent, "(num)")
299*1c12ee1eSDan Willemsen		g.P("return nil")
300*1c12ee1eSDan Willemsen		g.P("}")
301*1c12ee1eSDan Willemsen		g.P()
302*1c12ee1eSDan Willemsen	}
303*1c12ee1eSDan Willemsen
304*1c12ee1eSDan Willemsen	// EnumDescriptor method.
305*1c12ee1eSDan Willemsen	if e.genRawDescMethod {
306*1c12ee1eSDan Willemsen		var indexes []string
307*1c12ee1eSDan Willemsen		for i := 1; i < len(e.Location.Path); i += 2 {
308*1c12ee1eSDan Willemsen			indexes = append(indexes, strconv.Itoa(int(e.Location.Path[i])))
309*1c12ee1eSDan Willemsen		}
310*1c12ee1eSDan Willemsen		g.P("// Deprecated: Use ", e.GoIdent, ".Descriptor instead.")
311*1c12ee1eSDan Willemsen		g.P("func (", e.GoIdent, ") EnumDescriptor() ([]byte, []int) {")
312*1c12ee1eSDan Willemsen		g.P("return ", rawDescVarName(f), "GZIP(), []int{", strings.Join(indexes, ","), "}")
313*1c12ee1eSDan Willemsen		g.P("}")
314*1c12ee1eSDan Willemsen		g.P()
315*1c12ee1eSDan Willemsen		f.needRawDesc = true
316*1c12ee1eSDan Willemsen	}
317*1c12ee1eSDan Willemsen}
318*1c12ee1eSDan Willemsen
319*1c12ee1eSDan Willemsenfunc genMessage(g *protogen.GeneratedFile, f *fileInfo, m *messageInfo) {
320*1c12ee1eSDan Willemsen	if m.Desc.IsMapEntry() {
321*1c12ee1eSDan Willemsen		return
322*1c12ee1eSDan Willemsen	}
323*1c12ee1eSDan Willemsen
324*1c12ee1eSDan Willemsen	// Message type declaration.
325*1c12ee1eSDan Willemsen	g.Annotate(m.GoIdent.GoName, m.Location)
326*1c12ee1eSDan Willemsen	leadingComments := appendDeprecationSuffix(m.Comments.Leading,
327*1c12ee1eSDan Willemsen		m.Desc.ParentFile(),
328*1c12ee1eSDan Willemsen		m.Desc.Options().(*descriptorpb.MessageOptions).GetDeprecated())
329*1c12ee1eSDan Willemsen	g.P(leadingComments,
330*1c12ee1eSDan Willemsen		"type ", m.GoIdent, " struct {")
331*1c12ee1eSDan Willemsen	genMessageFields(g, f, m)
332*1c12ee1eSDan Willemsen	g.P("}")
333*1c12ee1eSDan Willemsen	g.P()
334*1c12ee1eSDan Willemsen
335*1c12ee1eSDan Willemsen	genMessageKnownFunctions(g, f, m)
336*1c12ee1eSDan Willemsen	genMessageDefaultDecls(g, f, m)
337*1c12ee1eSDan Willemsen	genMessageMethods(g, f, m)
338*1c12ee1eSDan Willemsen	genMessageOneofWrapperTypes(g, f, m)
339*1c12ee1eSDan Willemsen}
340*1c12ee1eSDan Willemsen
341*1c12ee1eSDan Willemsenfunc genMessageFields(g *protogen.GeneratedFile, f *fileInfo, m *messageInfo) {
342*1c12ee1eSDan Willemsen	sf := f.allMessageFieldsByPtr[m]
343*1c12ee1eSDan Willemsen	genMessageInternalFields(g, f, m, sf)
344*1c12ee1eSDan Willemsen	for _, field := range m.Fields {
345*1c12ee1eSDan Willemsen		genMessageField(g, f, m, field, sf)
346*1c12ee1eSDan Willemsen	}
347*1c12ee1eSDan Willemsen}
348*1c12ee1eSDan Willemsen
349*1c12ee1eSDan Willemsenfunc genMessageInternalFields(g *protogen.GeneratedFile, f *fileInfo, m *messageInfo, sf *structFields) {
350*1c12ee1eSDan Willemsen	g.P(genid.State_goname, " ", protoimplPackage.Ident("MessageState"))
351*1c12ee1eSDan Willemsen	sf.append(genid.State_goname)
352*1c12ee1eSDan Willemsen	g.P(genid.SizeCache_goname, " ", protoimplPackage.Ident("SizeCache"))
353*1c12ee1eSDan Willemsen	sf.append(genid.SizeCache_goname)
354*1c12ee1eSDan Willemsen	if m.hasWeak {
355*1c12ee1eSDan Willemsen		g.P(genid.WeakFields_goname, " ", protoimplPackage.Ident("WeakFields"))
356*1c12ee1eSDan Willemsen		sf.append(genid.WeakFields_goname)
357*1c12ee1eSDan Willemsen	}
358*1c12ee1eSDan Willemsen	g.P(genid.UnknownFields_goname, " ", protoimplPackage.Ident("UnknownFields"))
359*1c12ee1eSDan Willemsen	sf.append(genid.UnknownFields_goname)
360*1c12ee1eSDan Willemsen	if m.Desc.ExtensionRanges().Len() > 0 {
361*1c12ee1eSDan Willemsen		g.P(genid.ExtensionFields_goname, " ", protoimplPackage.Ident("ExtensionFields"))
362*1c12ee1eSDan Willemsen		sf.append(genid.ExtensionFields_goname)
363*1c12ee1eSDan Willemsen	}
364*1c12ee1eSDan Willemsen	if sf.count > 0 {
365*1c12ee1eSDan Willemsen		g.P()
366*1c12ee1eSDan Willemsen	}
367*1c12ee1eSDan Willemsen}
368*1c12ee1eSDan Willemsen
369*1c12ee1eSDan Willemsenfunc genMessageField(g *protogen.GeneratedFile, f *fileInfo, m *messageInfo, field *protogen.Field, sf *structFields) {
370*1c12ee1eSDan Willemsen	if oneof := field.Oneof; oneof != nil && !oneof.Desc.IsSynthetic() {
371*1c12ee1eSDan Willemsen		// It would be a bit simpler to iterate over the oneofs below,
372*1c12ee1eSDan Willemsen		// but generating the field here keeps the contents of the Go
373*1c12ee1eSDan Willemsen		// struct in the same order as the contents of the source
374*1c12ee1eSDan Willemsen		// .proto file.
375*1c12ee1eSDan Willemsen		if oneof.Fields[0] != field {
376*1c12ee1eSDan Willemsen			return // only generate for first appearance
377*1c12ee1eSDan Willemsen		}
378*1c12ee1eSDan Willemsen
379*1c12ee1eSDan Willemsen		tags := structTags{
380*1c12ee1eSDan Willemsen			{"protobuf_oneof", string(oneof.Desc.Name())},
381*1c12ee1eSDan Willemsen		}
382*1c12ee1eSDan Willemsen		if m.isTracked {
383*1c12ee1eSDan Willemsen			tags = append(tags, gotrackTags...)
384*1c12ee1eSDan Willemsen		}
385*1c12ee1eSDan Willemsen
386*1c12ee1eSDan Willemsen		g.Annotate(m.GoIdent.GoName+"."+oneof.GoName, oneof.Location)
387*1c12ee1eSDan Willemsen		leadingComments := oneof.Comments.Leading
388*1c12ee1eSDan Willemsen		if leadingComments != "" {
389*1c12ee1eSDan Willemsen			leadingComments += "\n"
390*1c12ee1eSDan Willemsen		}
391*1c12ee1eSDan Willemsen		ss := []string{fmt.Sprintf(" Types that are assignable to %s:\n", oneof.GoName)}
392*1c12ee1eSDan Willemsen		for _, field := range oneof.Fields {
393*1c12ee1eSDan Willemsen			ss = append(ss, "\t*"+field.GoIdent.GoName+"\n")
394*1c12ee1eSDan Willemsen		}
395*1c12ee1eSDan Willemsen		leadingComments += protogen.Comments(strings.Join(ss, ""))
396*1c12ee1eSDan Willemsen		g.P(leadingComments,
397*1c12ee1eSDan Willemsen			oneof.GoName, " ", oneofInterfaceName(oneof), tags)
398*1c12ee1eSDan Willemsen		sf.append(oneof.GoName)
399*1c12ee1eSDan Willemsen		return
400*1c12ee1eSDan Willemsen	}
401*1c12ee1eSDan Willemsen	goType, pointer := fieldGoType(g, f, field)
402*1c12ee1eSDan Willemsen	if pointer {
403*1c12ee1eSDan Willemsen		goType = "*" + goType
404*1c12ee1eSDan Willemsen	}
405*1c12ee1eSDan Willemsen	tags := structTags{
406*1c12ee1eSDan Willemsen		{"protobuf", fieldProtobufTagValue(field)},
407*1c12ee1eSDan Willemsen		{"json", fieldJSONTagValue(field)},
408*1c12ee1eSDan Willemsen	}
409*1c12ee1eSDan Willemsen	if field.Desc.IsMap() {
410*1c12ee1eSDan Willemsen		key := field.Message.Fields[0]
411*1c12ee1eSDan Willemsen		val := field.Message.Fields[1]
412*1c12ee1eSDan Willemsen		tags = append(tags, structTags{
413*1c12ee1eSDan Willemsen			{"protobuf_key", fieldProtobufTagValue(key)},
414*1c12ee1eSDan Willemsen			{"protobuf_val", fieldProtobufTagValue(val)},
415*1c12ee1eSDan Willemsen		}...)
416*1c12ee1eSDan Willemsen	}
417*1c12ee1eSDan Willemsen	if m.isTracked {
418*1c12ee1eSDan Willemsen		tags = append(tags, gotrackTags...)
419*1c12ee1eSDan Willemsen	}
420*1c12ee1eSDan Willemsen
421*1c12ee1eSDan Willemsen	name := field.GoName
422*1c12ee1eSDan Willemsen	if field.Desc.IsWeak() {
423*1c12ee1eSDan Willemsen		name = genid.WeakFieldPrefix_goname + name
424*1c12ee1eSDan Willemsen	}
425*1c12ee1eSDan Willemsen	g.Annotate(m.GoIdent.GoName+"."+name, field.Location)
426*1c12ee1eSDan Willemsen	leadingComments := appendDeprecationSuffix(field.Comments.Leading,
427*1c12ee1eSDan Willemsen		field.Desc.ParentFile(),
428*1c12ee1eSDan Willemsen		field.Desc.Options().(*descriptorpb.FieldOptions).GetDeprecated())
429*1c12ee1eSDan Willemsen	g.P(leadingComments,
430*1c12ee1eSDan Willemsen		name, " ", goType, tags,
431*1c12ee1eSDan Willemsen		trailingComment(field.Comments.Trailing))
432*1c12ee1eSDan Willemsen	sf.append(field.GoName)
433*1c12ee1eSDan Willemsen}
434*1c12ee1eSDan Willemsen
435*1c12ee1eSDan Willemsen// genMessageDefaultDecls generates consts and vars holding the default
436*1c12ee1eSDan Willemsen// values of fields.
437*1c12ee1eSDan Willemsenfunc genMessageDefaultDecls(g *protogen.GeneratedFile, f *fileInfo, m *messageInfo) {
438*1c12ee1eSDan Willemsen	var consts, vars []string
439*1c12ee1eSDan Willemsen	for _, field := range m.Fields {
440*1c12ee1eSDan Willemsen		if !field.Desc.HasDefault() {
441*1c12ee1eSDan Willemsen			continue
442*1c12ee1eSDan Willemsen		}
443*1c12ee1eSDan Willemsen		name := "Default_" + m.GoIdent.GoName + "_" + field.GoName
444*1c12ee1eSDan Willemsen		goType, _ := fieldGoType(g, f, field)
445*1c12ee1eSDan Willemsen		defVal := field.Desc.Default()
446*1c12ee1eSDan Willemsen		switch field.Desc.Kind() {
447*1c12ee1eSDan Willemsen		case protoreflect.StringKind:
448*1c12ee1eSDan Willemsen			consts = append(consts, fmt.Sprintf("%s = %s(%q)", name, goType, defVal.String()))
449*1c12ee1eSDan Willemsen		case protoreflect.BytesKind:
450*1c12ee1eSDan Willemsen			vars = append(vars, fmt.Sprintf("%s = %s(%q)", name, goType, defVal.Bytes()))
451*1c12ee1eSDan Willemsen		case protoreflect.EnumKind:
452*1c12ee1eSDan Willemsen			idx := field.Desc.DefaultEnumValue().Index()
453*1c12ee1eSDan Willemsen			val := field.Enum.Values[idx]
454*1c12ee1eSDan Willemsen			if val.GoIdent.GoImportPath == f.GoImportPath {
455*1c12ee1eSDan Willemsen				consts = append(consts, fmt.Sprintf("%s = %s", name, g.QualifiedGoIdent(val.GoIdent)))
456*1c12ee1eSDan Willemsen			} else {
457*1c12ee1eSDan Willemsen				// If the enum value is declared in a different Go package,
458*1c12ee1eSDan Willemsen				// reference it by number since the name may not be correct.
459*1c12ee1eSDan Willemsen				// See https://github.com/golang/protobuf/issues/513.
460*1c12ee1eSDan Willemsen				consts = append(consts, fmt.Sprintf("%s = %s(%d) // %s",
461*1c12ee1eSDan Willemsen					name, g.QualifiedGoIdent(field.Enum.GoIdent), val.Desc.Number(), g.QualifiedGoIdent(val.GoIdent)))
462*1c12ee1eSDan Willemsen			}
463*1c12ee1eSDan Willemsen		case protoreflect.FloatKind, protoreflect.DoubleKind:
464*1c12ee1eSDan Willemsen			if f := defVal.Float(); math.IsNaN(f) || math.IsInf(f, 0) {
465*1c12ee1eSDan Willemsen				var fn, arg string
466*1c12ee1eSDan Willemsen				switch f := defVal.Float(); {
467*1c12ee1eSDan Willemsen				case math.IsInf(f, -1):
468*1c12ee1eSDan Willemsen					fn, arg = g.QualifiedGoIdent(mathPackage.Ident("Inf")), "-1"
469*1c12ee1eSDan Willemsen				case math.IsInf(f, +1):
470*1c12ee1eSDan Willemsen					fn, arg = g.QualifiedGoIdent(mathPackage.Ident("Inf")), "+1"
471*1c12ee1eSDan Willemsen				case math.IsNaN(f):
472*1c12ee1eSDan Willemsen					fn, arg = g.QualifiedGoIdent(mathPackage.Ident("NaN")), ""
473*1c12ee1eSDan Willemsen				}
474*1c12ee1eSDan Willemsen				vars = append(vars, fmt.Sprintf("%s = %s(%s(%s))", name, goType, fn, arg))
475*1c12ee1eSDan Willemsen			} else {
476*1c12ee1eSDan Willemsen				consts = append(consts, fmt.Sprintf("%s = %s(%v)", name, goType, f))
477*1c12ee1eSDan Willemsen			}
478*1c12ee1eSDan Willemsen		default:
479*1c12ee1eSDan Willemsen			consts = append(consts, fmt.Sprintf("%s = %s(%v)", name, goType, defVal.Interface()))
480*1c12ee1eSDan Willemsen		}
481*1c12ee1eSDan Willemsen	}
482*1c12ee1eSDan Willemsen	if len(consts) > 0 {
483*1c12ee1eSDan Willemsen		g.P("// Default values for ", m.GoIdent, " fields.")
484*1c12ee1eSDan Willemsen		g.P("const (")
485*1c12ee1eSDan Willemsen		for _, s := range consts {
486*1c12ee1eSDan Willemsen			g.P(s)
487*1c12ee1eSDan Willemsen		}
488*1c12ee1eSDan Willemsen		g.P(")")
489*1c12ee1eSDan Willemsen	}
490*1c12ee1eSDan Willemsen	if len(vars) > 0 {
491*1c12ee1eSDan Willemsen		g.P("// Default values for ", m.GoIdent, " fields.")
492*1c12ee1eSDan Willemsen		g.P("var (")
493*1c12ee1eSDan Willemsen		for _, s := range vars {
494*1c12ee1eSDan Willemsen			g.P(s)
495*1c12ee1eSDan Willemsen		}
496*1c12ee1eSDan Willemsen		g.P(")")
497*1c12ee1eSDan Willemsen	}
498*1c12ee1eSDan Willemsen	g.P()
499*1c12ee1eSDan Willemsen}
500*1c12ee1eSDan Willemsen
501*1c12ee1eSDan Willemsenfunc genMessageMethods(g *protogen.GeneratedFile, f *fileInfo, m *messageInfo) {
502*1c12ee1eSDan Willemsen	genMessageBaseMethods(g, f, m)
503*1c12ee1eSDan Willemsen	genMessageGetterMethods(g, f, m)
504*1c12ee1eSDan Willemsen	genMessageSetterMethods(g, f, m)
505*1c12ee1eSDan Willemsen}
506*1c12ee1eSDan Willemsen
507*1c12ee1eSDan Willemsenfunc genMessageBaseMethods(g *protogen.GeneratedFile, f *fileInfo, m *messageInfo) {
508*1c12ee1eSDan Willemsen	// Reset method.
509*1c12ee1eSDan Willemsen	g.P("func (x *", m.GoIdent, ") Reset() {")
510*1c12ee1eSDan Willemsen	g.P("*x = ", m.GoIdent, "{}")
511*1c12ee1eSDan Willemsen	g.P("if ", protoimplPackage.Ident("UnsafeEnabled"), " {")
512*1c12ee1eSDan Willemsen	g.P("mi := &", messageTypesVarName(f), "[", f.allMessagesByPtr[m], "]")
513*1c12ee1eSDan Willemsen	g.P("ms := ", protoimplPackage.Ident("X"), ".MessageStateOf(", protoimplPackage.Ident("Pointer"), "(x))")
514*1c12ee1eSDan Willemsen	g.P("ms.StoreMessageInfo(mi)")
515*1c12ee1eSDan Willemsen	g.P("}")
516*1c12ee1eSDan Willemsen	g.P("}")
517*1c12ee1eSDan Willemsen	g.P()
518*1c12ee1eSDan Willemsen
519*1c12ee1eSDan Willemsen	// String method.
520*1c12ee1eSDan Willemsen	g.P("func (x *", m.GoIdent, ") String() string {")
521*1c12ee1eSDan Willemsen	g.P("return ", protoimplPackage.Ident("X"), ".MessageStringOf(x)")
522*1c12ee1eSDan Willemsen	g.P("}")
523*1c12ee1eSDan Willemsen	g.P()
524*1c12ee1eSDan Willemsen
525*1c12ee1eSDan Willemsen	// ProtoMessage method.
526*1c12ee1eSDan Willemsen	g.P("func (*", m.GoIdent, ") ProtoMessage() {}")
527*1c12ee1eSDan Willemsen	g.P()
528*1c12ee1eSDan Willemsen
529*1c12ee1eSDan Willemsen	// ProtoReflect method.
530*1c12ee1eSDan Willemsen	genMessageReflectMethods(g, f, m)
531*1c12ee1eSDan Willemsen
532*1c12ee1eSDan Willemsen	// Descriptor method.
533*1c12ee1eSDan Willemsen	if m.genRawDescMethod {
534*1c12ee1eSDan Willemsen		var indexes []string
535*1c12ee1eSDan Willemsen		for i := 1; i < len(m.Location.Path); i += 2 {
536*1c12ee1eSDan Willemsen			indexes = append(indexes, strconv.Itoa(int(m.Location.Path[i])))
537*1c12ee1eSDan Willemsen		}
538*1c12ee1eSDan Willemsen		g.P("// Deprecated: Use ", m.GoIdent, ".ProtoReflect.Descriptor instead.")
539*1c12ee1eSDan Willemsen		g.P("func (*", m.GoIdent, ") Descriptor() ([]byte, []int) {")
540*1c12ee1eSDan Willemsen		g.P("return ", rawDescVarName(f), "GZIP(), []int{", strings.Join(indexes, ","), "}")
541*1c12ee1eSDan Willemsen		g.P("}")
542*1c12ee1eSDan Willemsen		g.P()
543*1c12ee1eSDan Willemsen		f.needRawDesc = true
544*1c12ee1eSDan Willemsen	}
545*1c12ee1eSDan Willemsen}
546*1c12ee1eSDan Willemsen
547*1c12ee1eSDan Willemsenfunc genMessageGetterMethods(g *protogen.GeneratedFile, f *fileInfo, m *messageInfo) {
548*1c12ee1eSDan Willemsen	for _, field := range m.Fields {
549*1c12ee1eSDan Willemsen		genNoInterfacePragma(g, m.isTracked)
550*1c12ee1eSDan Willemsen
551*1c12ee1eSDan Willemsen		// Getter for parent oneof.
552*1c12ee1eSDan Willemsen		if oneof := field.Oneof; oneof != nil && oneof.Fields[0] == field && !oneof.Desc.IsSynthetic() {
553*1c12ee1eSDan Willemsen			g.Annotate(m.GoIdent.GoName+".Get"+oneof.GoName, oneof.Location)
554*1c12ee1eSDan Willemsen			g.P("func (m *", m.GoIdent.GoName, ") Get", oneof.GoName, "() ", oneofInterfaceName(oneof), " {")
555*1c12ee1eSDan Willemsen			g.P("if m != nil {")
556*1c12ee1eSDan Willemsen			g.P("return m.", oneof.GoName)
557*1c12ee1eSDan Willemsen			g.P("}")
558*1c12ee1eSDan Willemsen			g.P("return nil")
559*1c12ee1eSDan Willemsen			g.P("}")
560*1c12ee1eSDan Willemsen			g.P()
561*1c12ee1eSDan Willemsen		}
562*1c12ee1eSDan Willemsen
563*1c12ee1eSDan Willemsen		// Getter for message field.
564*1c12ee1eSDan Willemsen		goType, pointer := fieldGoType(g, f, field)
565*1c12ee1eSDan Willemsen		defaultValue := fieldDefaultValue(g, f, m, field)
566*1c12ee1eSDan Willemsen		g.Annotate(m.GoIdent.GoName+".Get"+field.GoName, field.Location)
567*1c12ee1eSDan Willemsen		leadingComments := appendDeprecationSuffix("",
568*1c12ee1eSDan Willemsen			field.Desc.ParentFile(),
569*1c12ee1eSDan Willemsen			field.Desc.Options().(*descriptorpb.FieldOptions).GetDeprecated())
570*1c12ee1eSDan Willemsen		switch {
571*1c12ee1eSDan Willemsen		case field.Desc.IsWeak():
572*1c12ee1eSDan Willemsen			g.P(leadingComments, "func (x *", m.GoIdent, ") Get", field.GoName, "() ", protoPackage.Ident("Message"), "{")
573*1c12ee1eSDan Willemsen			g.P("var w ", protoimplPackage.Ident("WeakFields"))
574*1c12ee1eSDan Willemsen			g.P("if x != nil {")
575*1c12ee1eSDan Willemsen			g.P("w = x.", genid.WeakFields_goname)
576*1c12ee1eSDan Willemsen			if m.isTracked {
577*1c12ee1eSDan Willemsen				g.P("_ = x.", genid.WeakFieldPrefix_goname+field.GoName)
578*1c12ee1eSDan Willemsen			}
579*1c12ee1eSDan Willemsen			g.P("}")
580*1c12ee1eSDan Willemsen			g.P("return ", protoimplPackage.Ident("X"), ".GetWeak(w, ", field.Desc.Number(), ", ", strconv.Quote(string(field.Message.Desc.FullName())), ")")
581*1c12ee1eSDan Willemsen			g.P("}")
582*1c12ee1eSDan Willemsen		case field.Oneof != nil && !field.Oneof.Desc.IsSynthetic():
583*1c12ee1eSDan Willemsen			g.P(leadingComments, "func (x *", m.GoIdent, ") Get", field.GoName, "() ", goType, " {")
584*1c12ee1eSDan Willemsen			g.P("if x, ok := x.Get", field.Oneof.GoName, "().(*", field.GoIdent, "); ok {")
585*1c12ee1eSDan Willemsen			g.P("return x.", field.GoName)
586*1c12ee1eSDan Willemsen			g.P("}")
587*1c12ee1eSDan Willemsen			g.P("return ", defaultValue)
588*1c12ee1eSDan Willemsen			g.P("}")
589*1c12ee1eSDan Willemsen		default:
590*1c12ee1eSDan Willemsen			g.P(leadingComments, "func (x *", m.GoIdent, ") Get", field.GoName, "() ", goType, " {")
591*1c12ee1eSDan Willemsen			if !field.Desc.HasPresence() || defaultValue == "nil" {
592*1c12ee1eSDan Willemsen				g.P("if x != nil {")
593*1c12ee1eSDan Willemsen			} else {
594*1c12ee1eSDan Willemsen				g.P("if x != nil && x.", field.GoName, " != nil {")
595*1c12ee1eSDan Willemsen			}
596*1c12ee1eSDan Willemsen			star := ""
597*1c12ee1eSDan Willemsen			if pointer {
598*1c12ee1eSDan Willemsen				star = "*"
599*1c12ee1eSDan Willemsen			}
600*1c12ee1eSDan Willemsen			g.P("return ", star, " x.", field.GoName)
601*1c12ee1eSDan Willemsen			g.P("}")
602*1c12ee1eSDan Willemsen			g.P("return ", defaultValue)
603*1c12ee1eSDan Willemsen			g.P("}")
604*1c12ee1eSDan Willemsen		}
605*1c12ee1eSDan Willemsen		g.P()
606*1c12ee1eSDan Willemsen	}
607*1c12ee1eSDan Willemsen}
608*1c12ee1eSDan Willemsen
609*1c12ee1eSDan Willemsenfunc genMessageSetterMethods(g *protogen.GeneratedFile, f *fileInfo, m *messageInfo) {
610*1c12ee1eSDan Willemsen	for _, field := range m.Fields {
611*1c12ee1eSDan Willemsen		if !field.Desc.IsWeak() {
612*1c12ee1eSDan Willemsen			continue
613*1c12ee1eSDan Willemsen		}
614*1c12ee1eSDan Willemsen
615*1c12ee1eSDan Willemsen		genNoInterfacePragma(g, m.isTracked)
616*1c12ee1eSDan Willemsen
617*1c12ee1eSDan Willemsen		g.Annotate(m.GoIdent.GoName+".Set"+field.GoName, field.Location)
618*1c12ee1eSDan Willemsen		leadingComments := appendDeprecationSuffix("",
619*1c12ee1eSDan Willemsen			field.Desc.ParentFile(),
620*1c12ee1eSDan Willemsen			field.Desc.Options().(*descriptorpb.FieldOptions).GetDeprecated())
621*1c12ee1eSDan Willemsen		g.P(leadingComments, "func (x *", m.GoIdent, ") Set", field.GoName, "(v ", protoPackage.Ident("Message"), ") {")
622*1c12ee1eSDan Willemsen		g.P("var w *", protoimplPackage.Ident("WeakFields"))
623*1c12ee1eSDan Willemsen		g.P("if x != nil {")
624*1c12ee1eSDan Willemsen		g.P("w = &x.", genid.WeakFields_goname)
625*1c12ee1eSDan Willemsen		if m.isTracked {
626*1c12ee1eSDan Willemsen			g.P("_ = x.", genid.WeakFieldPrefix_goname+field.GoName)
627*1c12ee1eSDan Willemsen		}
628*1c12ee1eSDan Willemsen		g.P("}")
629*1c12ee1eSDan Willemsen		g.P(protoimplPackage.Ident("X"), ".SetWeak(w, ", field.Desc.Number(), ", ", strconv.Quote(string(field.Message.Desc.FullName())), ", v)")
630*1c12ee1eSDan Willemsen		g.P("}")
631*1c12ee1eSDan Willemsen		g.P()
632*1c12ee1eSDan Willemsen	}
633*1c12ee1eSDan Willemsen}
634*1c12ee1eSDan Willemsen
635*1c12ee1eSDan Willemsen// fieldGoType returns the Go type used for a field.
636*1c12ee1eSDan Willemsen//
637*1c12ee1eSDan Willemsen// If it returns pointer=true, the struct field is a pointer to the type.
638*1c12ee1eSDan Willemsenfunc fieldGoType(g *protogen.GeneratedFile, f *fileInfo, field *protogen.Field) (goType string, pointer bool) {
639*1c12ee1eSDan Willemsen	if field.Desc.IsWeak() {
640*1c12ee1eSDan Willemsen		return "struct{}", false
641*1c12ee1eSDan Willemsen	}
642*1c12ee1eSDan Willemsen
643*1c12ee1eSDan Willemsen	pointer = field.Desc.HasPresence()
644*1c12ee1eSDan Willemsen	switch field.Desc.Kind() {
645*1c12ee1eSDan Willemsen	case protoreflect.BoolKind:
646*1c12ee1eSDan Willemsen		goType = "bool"
647*1c12ee1eSDan Willemsen	case protoreflect.EnumKind:
648*1c12ee1eSDan Willemsen		goType = g.QualifiedGoIdent(field.Enum.GoIdent)
649*1c12ee1eSDan Willemsen	case protoreflect.Int32Kind, protoreflect.Sint32Kind, protoreflect.Sfixed32Kind:
650*1c12ee1eSDan Willemsen		goType = "int32"
651*1c12ee1eSDan Willemsen	case protoreflect.Uint32Kind, protoreflect.Fixed32Kind:
652*1c12ee1eSDan Willemsen		goType = "uint32"
653*1c12ee1eSDan Willemsen	case protoreflect.Int64Kind, protoreflect.Sint64Kind, protoreflect.Sfixed64Kind:
654*1c12ee1eSDan Willemsen		goType = "int64"
655*1c12ee1eSDan Willemsen	case protoreflect.Uint64Kind, protoreflect.Fixed64Kind:
656*1c12ee1eSDan Willemsen		goType = "uint64"
657*1c12ee1eSDan Willemsen	case protoreflect.FloatKind:
658*1c12ee1eSDan Willemsen		goType = "float32"
659*1c12ee1eSDan Willemsen	case protoreflect.DoubleKind:
660*1c12ee1eSDan Willemsen		goType = "float64"
661*1c12ee1eSDan Willemsen	case protoreflect.StringKind:
662*1c12ee1eSDan Willemsen		goType = "string"
663*1c12ee1eSDan Willemsen	case protoreflect.BytesKind:
664*1c12ee1eSDan Willemsen		goType = "[]byte"
665*1c12ee1eSDan Willemsen		pointer = false // rely on nullability of slices for presence
666*1c12ee1eSDan Willemsen	case protoreflect.MessageKind, protoreflect.GroupKind:
667*1c12ee1eSDan Willemsen		goType = "*" + g.QualifiedGoIdent(field.Message.GoIdent)
668*1c12ee1eSDan Willemsen		pointer = false // pointer captured as part of the type
669*1c12ee1eSDan Willemsen	}
670*1c12ee1eSDan Willemsen	switch {
671*1c12ee1eSDan Willemsen	case field.Desc.IsList():
672*1c12ee1eSDan Willemsen		return "[]" + goType, false
673*1c12ee1eSDan Willemsen	case field.Desc.IsMap():
674*1c12ee1eSDan Willemsen		keyType, _ := fieldGoType(g, f, field.Message.Fields[0])
675*1c12ee1eSDan Willemsen		valType, _ := fieldGoType(g, f, field.Message.Fields[1])
676*1c12ee1eSDan Willemsen		return fmt.Sprintf("map[%v]%v", keyType, valType), false
677*1c12ee1eSDan Willemsen	}
678*1c12ee1eSDan Willemsen	return goType, pointer
679*1c12ee1eSDan Willemsen}
680*1c12ee1eSDan Willemsen
681*1c12ee1eSDan Willemsenfunc fieldProtobufTagValue(field *protogen.Field) string {
682*1c12ee1eSDan Willemsen	var enumName string
683*1c12ee1eSDan Willemsen	if field.Desc.Kind() == protoreflect.EnumKind {
684*1c12ee1eSDan Willemsen		enumName = protoimpl.X.LegacyEnumName(field.Enum.Desc)
685*1c12ee1eSDan Willemsen	}
686*1c12ee1eSDan Willemsen	return tag.Marshal(field.Desc, enumName)
687*1c12ee1eSDan Willemsen}
688*1c12ee1eSDan Willemsen
689*1c12ee1eSDan Willemsenfunc fieldDefaultValue(g *protogen.GeneratedFile, f *fileInfo, m *messageInfo, field *protogen.Field) string {
690*1c12ee1eSDan Willemsen	if field.Desc.IsList() {
691*1c12ee1eSDan Willemsen		return "nil"
692*1c12ee1eSDan Willemsen	}
693*1c12ee1eSDan Willemsen	if field.Desc.HasDefault() {
694*1c12ee1eSDan Willemsen		defVarName := "Default_" + m.GoIdent.GoName + "_" + field.GoName
695*1c12ee1eSDan Willemsen		if field.Desc.Kind() == protoreflect.BytesKind {
696*1c12ee1eSDan Willemsen			return "append([]byte(nil), " + defVarName + "...)"
697*1c12ee1eSDan Willemsen		}
698*1c12ee1eSDan Willemsen		return defVarName
699*1c12ee1eSDan Willemsen	}
700*1c12ee1eSDan Willemsen	switch field.Desc.Kind() {
701*1c12ee1eSDan Willemsen	case protoreflect.BoolKind:
702*1c12ee1eSDan Willemsen		return "false"
703*1c12ee1eSDan Willemsen	case protoreflect.StringKind:
704*1c12ee1eSDan Willemsen		return `""`
705*1c12ee1eSDan Willemsen	case protoreflect.MessageKind, protoreflect.GroupKind, protoreflect.BytesKind:
706*1c12ee1eSDan Willemsen		return "nil"
707*1c12ee1eSDan Willemsen	case protoreflect.EnumKind:
708*1c12ee1eSDan Willemsen		val := field.Enum.Values[0]
709*1c12ee1eSDan Willemsen		if val.GoIdent.GoImportPath == f.GoImportPath {
710*1c12ee1eSDan Willemsen			return g.QualifiedGoIdent(val.GoIdent)
711*1c12ee1eSDan Willemsen		} else {
712*1c12ee1eSDan Willemsen			// If the enum value is declared in a different Go package,
713*1c12ee1eSDan Willemsen			// reference it by number since the name may not be correct.
714*1c12ee1eSDan Willemsen			// See https://github.com/golang/protobuf/issues/513.
715*1c12ee1eSDan Willemsen			return g.QualifiedGoIdent(field.Enum.GoIdent) + "(" + strconv.FormatInt(int64(val.Desc.Number()), 10) + ")"
716*1c12ee1eSDan Willemsen		}
717*1c12ee1eSDan Willemsen	default:
718*1c12ee1eSDan Willemsen		return "0"
719*1c12ee1eSDan Willemsen	}
720*1c12ee1eSDan Willemsen}
721*1c12ee1eSDan Willemsen
722*1c12ee1eSDan Willemsenfunc fieldJSONTagValue(field *protogen.Field) string {
723*1c12ee1eSDan Willemsen	return string(field.Desc.Name()) + ",omitempty"
724*1c12ee1eSDan Willemsen}
725*1c12ee1eSDan Willemsen
726*1c12ee1eSDan Willemsenfunc genExtensions(g *protogen.GeneratedFile, f *fileInfo) {
727*1c12ee1eSDan Willemsen	if len(f.allExtensions) == 0 {
728*1c12ee1eSDan Willemsen		return
729*1c12ee1eSDan Willemsen	}
730*1c12ee1eSDan Willemsen
731*1c12ee1eSDan Willemsen	g.P("var ", extensionTypesVarName(f), " = []", protoimplPackage.Ident("ExtensionInfo"), "{")
732*1c12ee1eSDan Willemsen	for _, x := range f.allExtensions {
733*1c12ee1eSDan Willemsen		g.P("{")
734*1c12ee1eSDan Willemsen		g.P("ExtendedType: (*", x.Extendee.GoIdent, ")(nil),")
735*1c12ee1eSDan Willemsen		goType, pointer := fieldGoType(g, f, x.Extension)
736*1c12ee1eSDan Willemsen		if pointer {
737*1c12ee1eSDan Willemsen			goType = "*" + goType
738*1c12ee1eSDan Willemsen		}
739*1c12ee1eSDan Willemsen		g.P("ExtensionType: (", goType, ")(nil),")
740*1c12ee1eSDan Willemsen		g.P("Field: ", x.Desc.Number(), ",")
741*1c12ee1eSDan Willemsen		g.P("Name: ", strconv.Quote(string(x.Desc.FullName())), ",")
742*1c12ee1eSDan Willemsen		g.P("Tag: ", strconv.Quote(fieldProtobufTagValue(x.Extension)), ",")
743*1c12ee1eSDan Willemsen		g.P("Filename: ", strconv.Quote(f.Desc.Path()), ",")
744*1c12ee1eSDan Willemsen		g.P("},")
745*1c12ee1eSDan Willemsen	}
746*1c12ee1eSDan Willemsen	g.P("}")
747*1c12ee1eSDan Willemsen	g.P()
748*1c12ee1eSDan Willemsen
749*1c12ee1eSDan Willemsen	// Group extensions by the target message.
750*1c12ee1eSDan Willemsen	var orderedTargets []protogen.GoIdent
751*1c12ee1eSDan Willemsen	allExtensionsByTarget := make(map[protogen.GoIdent][]*extensionInfo)
752*1c12ee1eSDan Willemsen	allExtensionsByPtr := make(map[*extensionInfo]int)
753*1c12ee1eSDan Willemsen	for i, x := range f.allExtensions {
754*1c12ee1eSDan Willemsen		target := x.Extendee.GoIdent
755*1c12ee1eSDan Willemsen		if len(allExtensionsByTarget[target]) == 0 {
756*1c12ee1eSDan Willemsen			orderedTargets = append(orderedTargets, target)
757*1c12ee1eSDan Willemsen		}
758*1c12ee1eSDan Willemsen		allExtensionsByTarget[target] = append(allExtensionsByTarget[target], x)
759*1c12ee1eSDan Willemsen		allExtensionsByPtr[x] = i
760*1c12ee1eSDan Willemsen	}
761*1c12ee1eSDan Willemsen	for _, target := range orderedTargets {
762*1c12ee1eSDan Willemsen		g.P("// Extension fields to ", target, ".")
763*1c12ee1eSDan Willemsen		g.P("var (")
764*1c12ee1eSDan Willemsen		for _, x := range allExtensionsByTarget[target] {
765*1c12ee1eSDan Willemsen			xd := x.Desc
766*1c12ee1eSDan Willemsen			typeName := xd.Kind().String()
767*1c12ee1eSDan Willemsen			switch xd.Kind() {
768*1c12ee1eSDan Willemsen			case protoreflect.EnumKind:
769*1c12ee1eSDan Willemsen				typeName = string(xd.Enum().FullName())
770*1c12ee1eSDan Willemsen			case protoreflect.MessageKind, protoreflect.GroupKind:
771*1c12ee1eSDan Willemsen				typeName = string(xd.Message().FullName())
772*1c12ee1eSDan Willemsen			}
773*1c12ee1eSDan Willemsen			fieldName := string(xd.Name())
774*1c12ee1eSDan Willemsen
775*1c12ee1eSDan Willemsen			leadingComments := x.Comments.Leading
776*1c12ee1eSDan Willemsen			if leadingComments != "" {
777*1c12ee1eSDan Willemsen				leadingComments += "\n"
778*1c12ee1eSDan Willemsen			}
779*1c12ee1eSDan Willemsen			leadingComments += protogen.Comments(fmt.Sprintf(" %v %v %v = %v;\n",
780*1c12ee1eSDan Willemsen				xd.Cardinality(), typeName, fieldName, xd.Number()))
781*1c12ee1eSDan Willemsen			leadingComments = appendDeprecationSuffix(leadingComments,
782*1c12ee1eSDan Willemsen				x.Desc.ParentFile(),
783*1c12ee1eSDan Willemsen				x.Desc.Options().(*descriptorpb.FieldOptions).GetDeprecated())
784*1c12ee1eSDan Willemsen			g.P(leadingComments,
785*1c12ee1eSDan Willemsen				"E_", x.GoIdent, " = &", extensionTypesVarName(f), "[", allExtensionsByPtr[x], "]",
786*1c12ee1eSDan Willemsen				trailingComment(x.Comments.Trailing))
787*1c12ee1eSDan Willemsen		}
788*1c12ee1eSDan Willemsen		g.P(")")
789*1c12ee1eSDan Willemsen		g.P()
790*1c12ee1eSDan Willemsen	}
791*1c12ee1eSDan Willemsen}
792*1c12ee1eSDan Willemsen
793*1c12ee1eSDan Willemsen// genMessageOneofWrapperTypes generates the oneof wrapper types and
794*1c12ee1eSDan Willemsen// associates the types with the parent message type.
795*1c12ee1eSDan Willemsenfunc genMessageOneofWrapperTypes(g *protogen.GeneratedFile, f *fileInfo, m *messageInfo) {
796*1c12ee1eSDan Willemsen	for _, oneof := range m.Oneofs {
797*1c12ee1eSDan Willemsen		if oneof.Desc.IsSynthetic() {
798*1c12ee1eSDan Willemsen			continue
799*1c12ee1eSDan Willemsen		}
800*1c12ee1eSDan Willemsen		ifName := oneofInterfaceName(oneof)
801*1c12ee1eSDan Willemsen		g.P("type ", ifName, " interface {")
802*1c12ee1eSDan Willemsen		g.P(ifName, "()")
803*1c12ee1eSDan Willemsen		g.P("}")
804*1c12ee1eSDan Willemsen		g.P()
805*1c12ee1eSDan Willemsen		for _, field := range oneof.Fields {
806*1c12ee1eSDan Willemsen			g.Annotate(field.GoIdent.GoName, field.Location)
807*1c12ee1eSDan Willemsen			g.Annotate(field.GoIdent.GoName+"."+field.GoName, field.Location)
808*1c12ee1eSDan Willemsen			g.P("type ", field.GoIdent, " struct {")
809*1c12ee1eSDan Willemsen			goType, _ := fieldGoType(g, f, field)
810*1c12ee1eSDan Willemsen			tags := structTags{
811*1c12ee1eSDan Willemsen				{"protobuf", fieldProtobufTagValue(field)},
812*1c12ee1eSDan Willemsen			}
813*1c12ee1eSDan Willemsen			if m.isTracked {
814*1c12ee1eSDan Willemsen				tags = append(tags, gotrackTags...)
815*1c12ee1eSDan Willemsen			}
816*1c12ee1eSDan Willemsen			leadingComments := appendDeprecationSuffix(field.Comments.Leading,
817*1c12ee1eSDan Willemsen				field.Desc.ParentFile(),
818*1c12ee1eSDan Willemsen				field.Desc.Options().(*descriptorpb.FieldOptions).GetDeprecated())
819*1c12ee1eSDan Willemsen			g.P(leadingComments,
820*1c12ee1eSDan Willemsen				field.GoName, " ", goType, tags,
821*1c12ee1eSDan Willemsen				trailingComment(field.Comments.Trailing))
822*1c12ee1eSDan Willemsen			g.P("}")
823*1c12ee1eSDan Willemsen			g.P()
824*1c12ee1eSDan Willemsen		}
825*1c12ee1eSDan Willemsen		for _, field := range oneof.Fields {
826*1c12ee1eSDan Willemsen			g.P("func (*", field.GoIdent, ") ", ifName, "() {}")
827*1c12ee1eSDan Willemsen			g.P()
828*1c12ee1eSDan Willemsen		}
829*1c12ee1eSDan Willemsen	}
830*1c12ee1eSDan Willemsen}
831*1c12ee1eSDan Willemsen
832*1c12ee1eSDan Willemsen// oneofInterfaceName returns the name of the interface type implemented by
833*1c12ee1eSDan Willemsen// the oneof field value types.
834*1c12ee1eSDan Willemsenfunc oneofInterfaceName(oneof *protogen.Oneof) string {
835*1c12ee1eSDan Willemsen	return "is" + oneof.GoIdent.GoName
836*1c12ee1eSDan Willemsen}
837*1c12ee1eSDan Willemsen
838*1c12ee1eSDan Willemsen// genNoInterfacePragma generates a standalone "nointerface" pragma to
839*1c12ee1eSDan Willemsen// decorate methods with field-tracking support.
840*1c12ee1eSDan Willemsenfunc genNoInterfacePragma(g *protogen.GeneratedFile, tracked bool) {
841*1c12ee1eSDan Willemsen	if tracked {
842*1c12ee1eSDan Willemsen		g.P("//go:nointerface")
843*1c12ee1eSDan Willemsen		g.P()
844*1c12ee1eSDan Willemsen	}
845*1c12ee1eSDan Willemsen}
846*1c12ee1eSDan Willemsen
847*1c12ee1eSDan Willemsenvar gotrackTags = structTags{{"go", "track"}}
848*1c12ee1eSDan Willemsen
849*1c12ee1eSDan Willemsen// structTags is a data structure for build idiomatic Go struct tags.
850*1c12ee1eSDan Willemsen// Each [2]string is a key-value pair, where value is the unescaped string.
851*1c12ee1eSDan Willemsen//
852*1c12ee1eSDan Willemsen// Example: structTags{{"key", "value"}}.String() -> `key:"value"`
853*1c12ee1eSDan Willemsentype structTags [][2]string
854*1c12ee1eSDan Willemsen
855*1c12ee1eSDan Willemsenfunc (tags structTags) String() string {
856*1c12ee1eSDan Willemsen	if len(tags) == 0 {
857*1c12ee1eSDan Willemsen		return ""
858*1c12ee1eSDan Willemsen	}
859*1c12ee1eSDan Willemsen	var ss []string
860*1c12ee1eSDan Willemsen	for _, tag := range tags {
861*1c12ee1eSDan Willemsen		// NOTE: When quoting the value, we need to make sure the backtick
862*1c12ee1eSDan Willemsen		// character does not appear. Convert all cases to the escaped hex form.
863*1c12ee1eSDan Willemsen		key := tag[0]
864*1c12ee1eSDan Willemsen		val := strings.Replace(strconv.Quote(tag[1]), "`", `\x60`, -1)
865*1c12ee1eSDan Willemsen		ss = append(ss, fmt.Sprintf("%s:%s", key, val))
866*1c12ee1eSDan Willemsen	}
867*1c12ee1eSDan Willemsen	return "`" + strings.Join(ss, " ") + "`"
868*1c12ee1eSDan Willemsen}
869*1c12ee1eSDan Willemsen
870*1c12ee1eSDan Willemsen// appendDeprecationSuffix optionally appends a deprecation notice as a suffix.
871*1c12ee1eSDan Willemsenfunc appendDeprecationSuffix(prefix protogen.Comments, parentFile protoreflect.FileDescriptor, deprecated bool) protogen.Comments {
872*1c12ee1eSDan Willemsen	fileDeprecated := parentFile.Options().(*descriptorpb.FileOptions).GetDeprecated()
873*1c12ee1eSDan Willemsen	if !deprecated && !fileDeprecated {
874*1c12ee1eSDan Willemsen		return prefix
875*1c12ee1eSDan Willemsen	}
876*1c12ee1eSDan Willemsen	if prefix != "" {
877*1c12ee1eSDan Willemsen		prefix += "\n"
878*1c12ee1eSDan Willemsen	}
879*1c12ee1eSDan Willemsen	if fileDeprecated {
880*1c12ee1eSDan Willemsen		return prefix + " Deprecated: The entire proto file " + protogen.Comments(parentFile.Path()) + " is marked as deprecated.\n"
881*1c12ee1eSDan Willemsen	}
882*1c12ee1eSDan Willemsen	return prefix + " Deprecated: Marked as deprecated in " + protogen.Comments(parentFile.Path()) + ".\n"
883*1c12ee1eSDan Willemsen}
884*1c12ee1eSDan Willemsen
885*1c12ee1eSDan Willemsen// trailingComment is like protogen.Comments, but lacks a trailing newline.
886*1c12ee1eSDan Willemsentype trailingComment protogen.Comments
887*1c12ee1eSDan Willemsen
888*1c12ee1eSDan Willemsenfunc (c trailingComment) String() string {
889*1c12ee1eSDan Willemsen	s := strings.TrimSuffix(protogen.Comments(c).String(), "\n")
890*1c12ee1eSDan Willemsen	if strings.Contains(s, "\n") {
891*1c12ee1eSDan Willemsen		// We don't support multi-lined trailing comments as it is unclear
892*1c12ee1eSDan Willemsen		// how to best render them in the generated code.
893*1c12ee1eSDan Willemsen		return ""
894*1c12ee1eSDan Willemsen	}
895*1c12ee1eSDan Willemsen	return s
896*1c12ee1eSDan Willemsen}
897