1package protoparse
2
3import (
4	"bytes"
5	"fmt"
6	"sort"
7	"strings"
8
9	"github.com/golang/protobuf/proto"
10	dpb "github.com/golang/protobuf/protoc-gen-go/descriptor"
11
12	"github.com/jhump/protoreflect/desc"
13	"github.com/jhump/protoreflect/desc/internal"
14	"github.com/jhump/protoreflect/desc/protoparse/ast"
15)
16
17type linker struct {
18	files          map[string]*parseResult
19	filenames      []string
20	errs           *errorHandler
21	descriptorPool map[*dpb.FileDescriptorProto]map[string]proto.Message
22	extensions     map[string]map[int32]string
23}
24
25func newLinker(files *parseResults, errs *errorHandler) *linker {
26	return &linker{files: files.resultsByFilename, filenames: files.filenames, errs: errs}
27}
28
29func (l *linker) linkFiles() (map[string]*desc.FileDescriptor, error) {
30	// First, we put all symbols into a single pool, which lets us ensure there
31	// are no duplicate symbols and will also let us resolve and revise all type
32	// references in next step.
33	if err := l.createDescriptorPool(); err != nil {
34		return nil, err
35	}
36
37	// After we've populated the pool, we can now try to resolve all type
38	// references. All references must be checked for correct type, any fields
39	// with enum types must be corrected (since we parse them as if they are
40	// message references since we don't actually know message or enum until
41	// link time), and references will be re-written to be fully-qualified
42	// references (e.g. start with a dot ".").
43	if err := l.resolveReferences(); err != nil {
44		return nil, err
45	}
46
47	if err := l.errs.getError(); err != nil {
48		// we won't be able to create real descriptors if we've encountered
49		// errors up to this point, so bail at this point
50		return nil, err
51	}
52
53	// Now we've validated the descriptors, so we can link them into rich
54	// descriptors. This is a little redundant since that step does similar
55	// checking of symbols. But, without breaking encapsulation (e.g. exporting
56	// a lot of fields from desc package that are currently unexported) or
57	// merging this into the same package, we can't really prevent it.
58	linked, err := l.createdLinkedDescriptors()
59	if err != nil {
60		return nil, err
61	}
62
63	// Now that we have linked descriptors, we can interpret any uninterpreted
64	// options that remain.
65	for _, r := range l.files {
66		fd := linked[r.fd.GetName()]
67		if err := interpretFileOptions(r, richFileDescriptorish{FileDescriptor: fd}); err != nil {
68			return nil, err
69		}
70		// we should now have any message_set_wire_format options parsed
71		// and can do further validation on tag ranges
72		if err := checkExtensionsInFile(fd, r); err != nil {
73			return nil, err
74		}
75	}
76
77	// When Parser calls linkFiles, it does not check errs again, and it expects that linkFiles
78	// will return all errors it should process. If the ErrorReporter handles all errors itself
79	// and always returns nil, we should get ErrInvalidSource here, and need to propagate this
80	if err := l.errs.getError(); err != nil {
81		return nil, err
82	}
83	return linked, nil
84}
85
86func (l *linker) createDescriptorPool() error {
87	l.descriptorPool = map[*dpb.FileDescriptorProto]map[string]proto.Message{}
88	for _, filename := range l.filenames {
89		r := l.files[filename]
90		fd := r.fd
91		pool := map[string]proto.Message{}
92		l.descriptorPool[fd] = pool
93		prefix := fd.GetPackage()
94		if prefix != "" {
95			prefix += "."
96		}
97		for _, md := range fd.MessageType {
98			if err := addMessageToPool(r, pool, l.errs, prefix, md); err != nil {
99				return err
100			}
101		}
102		for _, fld := range fd.Extension {
103			if err := addFieldToPool(r, pool, l.errs, prefix, fld); err != nil {
104				return err
105			}
106		}
107		for _, ed := range fd.EnumType {
108			if err := addEnumToPool(r, pool, l.errs, prefix, ed); err != nil {
109				return err
110			}
111		}
112		for _, sd := range fd.Service {
113			if err := addServiceToPool(r, pool, l.errs, prefix, sd); err != nil {
114				return err
115			}
116		}
117	}
118	// try putting everything into a single pool, to ensure there are no duplicates
119	// across files (e.g. same symbol, but declared in two different files)
120	type entry struct {
121		file string
122		msg  proto.Message
123	}
124	pool := map[string]entry{}
125	for _, filename := range l.filenames {
126		f := l.files[filename].fd
127		p := l.descriptorPool[f]
128		keys := make([]string, 0, len(p))
129		for k := range p {
130			keys = append(keys, k)
131		}
132		sort.Strings(keys) // for deterministic error reporting
133		for _, k := range keys {
134			v := p[k]
135			if e, ok := pool[k]; ok {
136				desc1 := e.msg
137				file1 := e.file
138				desc2 := v
139				file2 := f.GetName()
140				if file2 < file1 {
141					file1, file2 = file2, file1
142					desc1, desc2 = desc2, desc1
143				}
144				node := l.files[file2].nodes[desc2]
145				if err := l.errs.handleErrorWithPos(node.Start(), "duplicate symbol %s: already defined as %s in %q", k, descriptorType(desc1), file1); err != nil {
146					return err
147				}
148			}
149			pool[k] = entry{file: f.GetName(), msg: v}
150		}
151	}
152
153	return nil
154}
155
156func addMessageToPool(r *parseResult, pool map[string]proto.Message, errs *errorHandler, prefix string, md *dpb.DescriptorProto) error {
157	fqn := prefix + md.GetName()
158	if err := addToPool(r, pool, errs, fqn, md); err != nil {
159		return err
160	}
161	prefix = fqn + "."
162	for _, fld := range md.Field {
163		if err := addFieldToPool(r, pool, errs, prefix, fld); err != nil {
164			return err
165		}
166	}
167	for _, fld := range md.Extension {
168		if err := addFieldToPool(r, pool, errs, prefix, fld); err != nil {
169			return err
170		}
171	}
172	for _, nmd := range md.NestedType {
173		if err := addMessageToPool(r, pool, errs, prefix, nmd); err != nil {
174			return err
175		}
176	}
177	for _, ed := range md.EnumType {
178		if err := addEnumToPool(r, pool, errs, prefix, ed); err != nil {
179			return err
180		}
181	}
182	return nil
183}
184
185func addFieldToPool(r *parseResult, pool map[string]proto.Message, errs *errorHandler, prefix string, fld *dpb.FieldDescriptorProto) error {
186	fqn := prefix + fld.GetName()
187	return addToPool(r, pool, errs, fqn, fld)
188}
189
190func addEnumToPool(r *parseResult, pool map[string]proto.Message, errs *errorHandler, prefix string, ed *dpb.EnumDescriptorProto) error {
191	fqn := prefix + ed.GetName()
192	if err := addToPool(r, pool, errs, fqn, ed); err != nil {
193		return err
194	}
195	for _, evd := range ed.Value {
196		vfqn := fqn + "." + evd.GetName()
197		if err := addToPool(r, pool, errs, vfqn, evd); err != nil {
198			return err
199		}
200	}
201	return nil
202}
203
204func addServiceToPool(r *parseResult, pool map[string]proto.Message, errs *errorHandler, prefix string, sd *dpb.ServiceDescriptorProto) error {
205	fqn := prefix + sd.GetName()
206	if err := addToPool(r, pool, errs, fqn, sd); err != nil {
207		return err
208	}
209	for _, mtd := range sd.Method {
210		mfqn := fqn + "." + mtd.GetName()
211		if err := addToPool(r, pool, errs, mfqn, mtd); err != nil {
212			return err
213		}
214	}
215	return nil
216}
217
218func addToPool(r *parseResult, pool map[string]proto.Message, errs *errorHandler, fqn string, dsc proto.Message) error {
219	if d, ok := pool[fqn]; ok {
220		node := r.nodes[dsc]
221		if err := errs.handleErrorWithPos(node.Start(), "duplicate symbol %s: already defined as %s", fqn, descriptorType(d)); err != nil {
222			return err
223		}
224	}
225	pool[fqn] = dsc
226	return nil
227}
228
229func descriptorType(m proto.Message) string {
230	switch m := m.(type) {
231	case *dpb.DescriptorProto:
232		return "message"
233	case *dpb.DescriptorProto_ExtensionRange:
234		return "extension range"
235	case *dpb.FieldDescriptorProto:
236		if m.GetExtendee() == "" {
237			return "field"
238		} else {
239			return "extension"
240		}
241	case *dpb.EnumDescriptorProto:
242		return "enum"
243	case *dpb.EnumValueDescriptorProto:
244		return "enum value"
245	case *dpb.ServiceDescriptorProto:
246		return "service"
247	case *dpb.MethodDescriptorProto:
248		return "method"
249	case *dpb.FileDescriptorProto:
250		return "file"
251	default:
252		// shouldn't be possible
253		return fmt.Sprintf("%T", m)
254	}
255}
256
257func (l *linker) resolveReferences() error {
258	l.extensions = map[string]map[int32]string{}
259	for _, filename := range l.filenames {
260		r := l.files[filename]
261		fd := r.fd
262		prefix := fd.GetPackage()
263		scopes := []scope{fileScope(fd, l)}
264		if prefix != "" {
265			prefix += "."
266		}
267		if fd.Options != nil {
268			if err := l.resolveOptions(r, fd, "file", fd.GetName(), proto.MessageName(fd.Options), fd.Options.UninterpretedOption, scopes); err != nil {
269				return err
270			}
271		}
272		for _, md := range fd.MessageType {
273			if err := l.resolveMessageTypes(r, fd, prefix, md, scopes); err != nil {
274				return err
275			}
276		}
277		for _, fld := range fd.Extension {
278			if err := l.resolveFieldTypes(r, fd, prefix, fld, scopes); err != nil {
279				return err
280			}
281		}
282		for _, ed := range fd.EnumType {
283			if err := l.resolveEnumTypes(r, fd, prefix, ed, scopes); err != nil {
284				return err
285			}
286		}
287		for _, sd := range fd.Service {
288			if err := l.resolveServiceTypes(r, fd, prefix, sd, scopes); err != nil {
289				return err
290			}
291		}
292	}
293	return nil
294}
295
296func (l *linker) resolveEnumTypes(r *parseResult, fd *dpb.FileDescriptorProto, prefix string, ed *dpb.EnumDescriptorProto, scopes []scope) error {
297	enumFqn := prefix + ed.GetName()
298	if ed.Options != nil {
299		if err := l.resolveOptions(r, fd, "enum", enumFqn, proto.MessageName(ed.Options), ed.Options.UninterpretedOption, scopes); err != nil {
300			return err
301		}
302	}
303	for _, evd := range ed.Value {
304		if evd.Options != nil {
305			evFqn := enumFqn + "." + evd.GetName()
306			if err := l.resolveOptions(r, fd, "enum value", evFqn, proto.MessageName(evd.Options), evd.Options.UninterpretedOption, scopes); err != nil {
307				return err
308			}
309		}
310	}
311	return nil
312}
313
314func (l *linker) resolveMessageTypes(r *parseResult, fd *dpb.FileDescriptorProto, prefix string, md *dpb.DescriptorProto, scopes []scope) error {
315	fqn := prefix + md.GetName()
316	scope := messageScope(fqn, isProto3(fd), l.descriptorPool[fd])
317	scopes = append(scopes, scope)
318	prefix = fqn + "."
319
320	if md.Options != nil {
321		if err := l.resolveOptions(r, fd, "message", fqn, proto.MessageName(md.Options), md.Options.UninterpretedOption, scopes); err != nil {
322			return err
323		}
324	}
325
326	for _, nmd := range md.NestedType {
327		if err := l.resolveMessageTypes(r, fd, prefix, nmd, scopes); err != nil {
328			return err
329		}
330	}
331	for _, ned := range md.EnumType {
332		if err := l.resolveEnumTypes(r, fd, prefix, ned, scopes); err != nil {
333			return err
334		}
335	}
336	for _, fld := range md.Field {
337		if err := l.resolveFieldTypes(r, fd, prefix, fld, scopes); err != nil {
338			return err
339		}
340	}
341	for _, fld := range md.Extension {
342		if err := l.resolveFieldTypes(r, fd, prefix, fld, scopes); err != nil {
343			return err
344		}
345	}
346	for _, er := range md.ExtensionRange {
347		if er.Options != nil {
348			erName := fmt.Sprintf("%s:%d-%d", fqn, er.GetStart(), er.GetEnd()-1)
349			if err := l.resolveOptions(r, fd, "extension range", erName, proto.MessageName(er.Options), er.Options.UninterpretedOption, scopes); err != nil {
350				return err
351			}
352		}
353	}
354	return nil
355}
356
357func (l *linker) resolveFieldTypes(r *parseResult, fd *dpb.FileDescriptorProto, prefix string, fld *dpb.FieldDescriptorProto, scopes []scope) error {
358	thisName := prefix + fld.GetName()
359	scope := fmt.Sprintf("field %s", thisName)
360	node := r.getFieldNode(fld)
361	elemType := "field"
362	if fld.GetExtendee() != "" {
363		elemType = "extension"
364		fqn, dsc, _ := l.resolve(fd, fld.GetExtendee(), isMessage, scopes)
365		if dsc == nil {
366			return l.errs.handleErrorWithPos(node.FieldExtendee().Start(), "unknown extendee type %s", fld.GetExtendee())
367		}
368		extd, ok := dsc.(*dpb.DescriptorProto)
369		if !ok {
370			otherType := descriptorType(dsc)
371			return l.errs.handleErrorWithPos(node.FieldExtendee().Start(), "extendee is invalid: %s is a %s, not a message", fqn, otherType)
372		}
373		fld.Extendee = proto.String("." + fqn)
374		// make sure the tag number is in range
375		found := false
376		tag := fld.GetNumber()
377		for _, rng := range extd.ExtensionRange {
378			if tag >= rng.GetStart() && tag < rng.GetEnd() {
379				found = true
380				break
381			}
382		}
383		if !found {
384			if err := l.errs.handleErrorWithPos(node.FieldTag().Start(), "%s: tag %d is not in valid range for extended type %s", scope, tag, fqn); err != nil {
385				return err
386			}
387		} else {
388			// make sure tag is not a duplicate
389			usedExtTags := l.extensions[fqn]
390			if usedExtTags == nil {
391				usedExtTags = map[int32]string{}
392				l.extensions[fqn] = usedExtTags
393			}
394			if other := usedExtTags[fld.GetNumber()]; other != "" {
395				if err := l.errs.handleErrorWithPos(node.FieldTag().Start(), "%s: duplicate extension: %s and %s are both using tag %d", scope, other, thisName, fld.GetNumber()); err != nil {
396					return err
397				}
398			} else {
399				usedExtTags[fld.GetNumber()] = thisName
400			}
401		}
402	}
403
404	if fld.Options != nil {
405		if err := l.resolveOptions(r, fd, elemType, thisName, proto.MessageName(fld.Options), fld.Options.UninterpretedOption, scopes); err != nil {
406			return err
407		}
408	}
409
410	if fld.GetTypeName() == "" {
411		// scalar type; no further resolution required
412		return nil
413	}
414
415	fqn, dsc, proto3 := l.resolve(fd, fld.GetTypeName(), isType, scopes)
416	if dsc == nil {
417		return l.errs.handleErrorWithPos(node.FieldType().Start(), "%s: unknown type %s", scope, fld.GetTypeName())
418	}
419	switch dsc := dsc.(type) {
420	case *dpb.DescriptorProto:
421		fld.TypeName = proto.String("." + fqn)
422		// if type was tentatively unset, we now know it's actually a message
423		if fld.Type == nil {
424			fld.Type = dpb.FieldDescriptorProto_TYPE_MESSAGE.Enum()
425		}
426	case *dpb.EnumDescriptorProto:
427		if fld.GetExtendee() == "" && isProto3(fd) && !proto3 {
428			// fields in a proto3 message cannot refer to proto2 enums
429			return l.errs.handleErrorWithPos(node.FieldType().Start(), "%s: cannot use proto2 enum %s in a proto3 message", scope, fld.GetTypeName())
430		}
431		fld.TypeName = proto.String("." + fqn)
432		// the type was tentatively unset, but now we know it's actually an enum
433		fld.Type = dpb.FieldDescriptorProto_TYPE_ENUM.Enum()
434	default:
435		otherType := descriptorType(dsc)
436		return l.errs.handleErrorWithPos(node.FieldType().Start(), "%s: invalid type: %s is a %s, not a message or enum", scope, fqn, otherType)
437	}
438	return nil
439}
440
441func (l *linker) resolveServiceTypes(r *parseResult, fd *dpb.FileDescriptorProto, prefix string, sd *dpb.ServiceDescriptorProto, scopes []scope) error {
442	thisName := prefix + sd.GetName()
443	if sd.Options != nil {
444		if err := l.resolveOptions(r, fd, "service", thisName, proto.MessageName(sd.Options), sd.Options.UninterpretedOption, scopes); err != nil {
445			return err
446		}
447	}
448
449	for _, mtd := range sd.Method {
450		if mtd.Options != nil {
451			if err := l.resolveOptions(r, fd, "method", thisName+"."+mtd.GetName(), proto.MessageName(mtd.Options), mtd.Options.UninterpretedOption, scopes); err != nil {
452				return err
453			}
454		}
455		scope := fmt.Sprintf("method %s.%s", thisName, mtd.GetName())
456		node := r.getMethodNode(mtd)
457		fqn, dsc, _ := l.resolve(fd, mtd.GetInputType(), isMessage, scopes)
458		if dsc == nil {
459			if err := l.errs.handleErrorWithPos(node.GetInputType().Start(), "%s: unknown request type %s", scope, mtd.GetInputType()); err != nil {
460				return err
461			}
462		} else if _, ok := dsc.(*dpb.DescriptorProto); !ok {
463			otherType := descriptorType(dsc)
464			if err := l.errs.handleErrorWithPos(node.GetInputType().Start(), "%s: invalid request type: %s is a %s, not a message", scope, fqn, otherType); err != nil {
465				return err
466			}
467		} else {
468			mtd.InputType = proto.String("." + fqn)
469		}
470
471		fqn, dsc, _ = l.resolve(fd, mtd.GetOutputType(), isMessage, scopes)
472		if dsc == nil {
473			if err := l.errs.handleErrorWithPos(node.GetOutputType().Start(), "%s: unknown response type %s", scope, mtd.GetOutputType()); err != nil {
474				return err
475			}
476		} else if _, ok := dsc.(*dpb.DescriptorProto); !ok {
477			otherType := descriptorType(dsc)
478			if err := l.errs.handleErrorWithPos(node.GetOutputType().Start(), "%s: invalid response type: %s is a %s, not a message", scope, fqn, otherType); err != nil {
479				return err
480			}
481		} else {
482			mtd.OutputType = proto.String("." + fqn)
483		}
484	}
485	return nil
486}
487
488func (l *linker) resolveOptions(r *parseResult, fd *dpb.FileDescriptorProto, elemType, elemName, optType string, opts []*dpb.UninterpretedOption, scopes []scope) error {
489	var scope string
490	if elemType != "file" {
491		scope = fmt.Sprintf("%s %s: ", elemType, elemName)
492	}
493opts:
494	for _, opt := range opts {
495		for _, nm := range opt.Name {
496			if nm.GetIsExtension() {
497				node := r.getOptionNamePartNode(nm)
498				fqn, dsc, _ := l.resolve(fd, nm.GetNamePart(), isField, scopes)
499				if dsc == nil {
500					if err := l.errs.handleErrorWithPos(node.Start(), "%sunknown extension %s", scope, nm.GetNamePart()); err != nil {
501						return err
502					}
503					continue opts
504				}
505				if ext, ok := dsc.(*dpb.FieldDescriptorProto); !ok {
506					otherType := descriptorType(dsc)
507					if err := l.errs.handleErrorWithPos(node.Start(), "%sinvalid extension: %s is a %s, not an extension", scope, nm.GetNamePart(), otherType); err != nil {
508						return err
509					}
510					continue opts
511				} else if ext.GetExtendee() == "" {
512					if err := l.errs.handleErrorWithPos(node.Start(), "%sinvalid extension: %s is a field but not an extension", scope, nm.GetNamePart()); err != nil {
513						return err
514					}
515					continue opts
516				}
517				nm.NamePart = proto.String("." + fqn)
518			}
519		}
520	}
521	return nil
522}
523
524func (l *linker) resolve(fd *dpb.FileDescriptorProto, name string, allowed func(proto.Message) bool, scopes []scope) (fqn string, element proto.Message, proto3 bool) {
525	if strings.HasPrefix(name, ".") {
526		// already fully-qualified
527		d, proto3 := l.findSymbol(fd, name[1:], false, map[*dpb.FileDescriptorProto]struct{}{})
528		if d != nil {
529			return name[1:], d, proto3
530		}
531	} else {
532		// unqualified, so we look in the enclosing (last) scope first and move
533		// towards outermost (first) scope, trying to resolve the symbol
534		var bestGuess proto.Message
535		var bestGuessFqn string
536		var bestGuessProto3 bool
537		for i := len(scopes) - 1; i >= 0; i-- {
538			fqn, d, proto3 := scopes[i](name)
539			if d != nil {
540				if allowed(d) {
541					return fqn, d, proto3
542				} else if bestGuess == nil {
543					bestGuess = d
544					bestGuessFqn = fqn
545					bestGuessProto3 = proto3
546				}
547			}
548		}
549		// we return best guess, even though it was not an allowed kind of
550		// descriptor, so caller can print a better error message (e.g.
551		// indicating that the name was found but that it's the wrong type)
552		return bestGuessFqn, bestGuess, bestGuessProto3
553	}
554	return "", nil, false
555}
556
557func isField(m proto.Message) bool {
558	_, ok := m.(*dpb.FieldDescriptorProto)
559	return ok
560}
561
562func isMessage(m proto.Message) bool {
563	_, ok := m.(*dpb.DescriptorProto)
564	return ok
565}
566
567func isType(m proto.Message) bool {
568	switch m.(type) {
569	case *dpb.DescriptorProto, *dpb.EnumDescriptorProto:
570		return true
571	}
572	return false
573}
574
575// scope represents a lexical scope in a proto file in which messages and enums
576// can be declared.
577type scope func(symbol string) (fqn string, element proto.Message, proto3 bool)
578
579func fileScope(fd *dpb.FileDescriptorProto, l *linker) scope {
580	// we search symbols in this file, but also symbols in other files that have
581	// the same package as this file or a "parent" package (in protobuf,
582	// packages are a hierarchy like C++ namespaces)
583	prefixes := internal.CreatePrefixList(fd.GetPackage())
584	return func(name string) (string, proto.Message, bool) {
585		for _, prefix := range prefixes {
586			var n string
587			if prefix == "" {
588				n = name
589			} else {
590				n = prefix + "." + name
591			}
592			d, proto3 := l.findSymbol(fd, n, false, map[*dpb.FileDescriptorProto]struct{}{})
593			if d != nil {
594				return n, d, proto3
595			}
596		}
597		return "", nil, false
598	}
599}
600
601func messageScope(messageName string, proto3 bool, filePool map[string]proto.Message) scope {
602	return func(name string) (string, proto.Message, bool) {
603		n := messageName + "." + name
604		if d, ok := filePool[n]; ok {
605			return n, d, proto3
606		}
607		return "", nil, false
608	}
609}
610
611func (l *linker) findSymbol(fd *dpb.FileDescriptorProto, name string, public bool, checked map[*dpb.FileDescriptorProto]struct{}) (element proto.Message, proto3 bool) {
612	if _, ok := checked[fd]; ok {
613		// already checked this one
614		return nil, false
615	}
616	checked[fd] = struct{}{}
617	d := l.descriptorPool[fd][name]
618	if d != nil {
619		return d, isProto3(fd)
620	}
621
622	// When public = false, we are searching only directly imported symbols. But we
623	// also need to search transitive public imports due to semantics of public imports.
624	if public {
625		for _, depIndex := range fd.PublicDependency {
626			dep := fd.Dependency[depIndex]
627			depres := l.files[dep]
628			if depres == nil {
629				// we'll catch this error later
630				continue
631			}
632			if d, proto3 := l.findSymbol(depres.fd, name, true, checked); d != nil {
633				return d, proto3
634			}
635		}
636	} else {
637		for _, dep := range fd.Dependency {
638			depres := l.files[dep]
639			if depres == nil {
640				// we'll catch this error later
641				continue
642			}
643			if d, proto3 := l.findSymbol(depres.fd, name, true, checked); d != nil {
644				return d, proto3
645			}
646		}
647	}
648
649	return nil, false
650}
651
652func isProto3(fd *dpb.FileDescriptorProto) bool {
653	return fd.GetSyntax() == "proto3"
654}
655
656func (l *linker) createdLinkedDescriptors() (map[string]*desc.FileDescriptor, error) {
657	names := make([]string, 0, len(l.files))
658	for name := range l.files {
659		names = append(names, name)
660	}
661	sort.Strings(names)
662	linked := map[string]*desc.FileDescriptor{}
663	for _, name := range names {
664		if _, err := l.linkFile(name, nil, nil, linked); err != nil {
665			return nil, err
666		}
667	}
668	return linked, nil
669}
670
671func (l *linker) linkFile(name string, rootImportLoc *SourcePos, seen []string, linked map[string]*desc.FileDescriptor) (*desc.FileDescriptor, error) {
672	// check for import cycle
673	for _, s := range seen {
674		if name == s {
675			var msg bytes.Buffer
676			first := true
677			for _, s := range seen {
678				if first {
679					first = false
680				} else {
681					msg.WriteString(" -> ")
682				}
683				_, _ = fmt.Fprintf(&msg, "%q", s)
684			}
685			_, _ = fmt.Fprintf(&msg, " -> %q", name)
686			return nil, ErrorWithSourcePos{
687				Underlying: fmt.Errorf("cycle found in imports: %s", msg.String()),
688				Pos:        rootImportLoc,
689			}
690		}
691	}
692	seen = append(seen, name)
693
694	if lfd, ok := linked[name]; ok {
695		// already linked
696		return lfd, nil
697	}
698	r := l.files[name]
699	if r == nil {
700		importer := seen[len(seen)-2] // len-1 is *this* file, before that is the one that imported it
701		return nil, fmt.Errorf("no descriptor found for %q, imported by %q", name, importer)
702	}
703	var deps []*desc.FileDescriptor
704	if rootImportLoc == nil {
705		// try to find a source location for this "root" import
706		decl := r.getFileNode(r.fd)
707		fnode, ok := decl.(*ast.FileNode)
708		if ok {
709			for _, decl := range fnode.Decls {
710				if dep, ok := decl.(*ast.ImportNode); ok {
711					ldep, err := l.linkFile(dep.Name.AsString(), dep.Name.Start(), seen, linked)
712					if err != nil {
713						return nil, err
714					}
715					deps = append(deps, ldep)
716				}
717			}
718		} else {
719			// no AST? just use the descriptor
720			for _, dep := range r.fd.Dependency {
721				ldep, err := l.linkFile(dep, decl.Start(), seen, linked)
722				if err != nil {
723					return nil, err
724				}
725				deps = append(deps, ldep)
726			}
727		}
728	} else {
729		// we can just use the descriptor since we don't need source location
730		// (we'll just attribute any import cycles found to the "root" import)
731		for _, dep := range r.fd.Dependency {
732			ldep, err := l.linkFile(dep, rootImportLoc, seen, linked)
733			if err != nil {
734				return nil, err
735			}
736			deps = append(deps, ldep)
737		}
738	}
739	lfd, err := desc.CreateFileDescriptor(r.fd, deps...)
740	if err != nil {
741		return nil, fmt.Errorf("error linking %q: %s", name, err)
742	}
743	linked[name] = lfd
744	return lfd, nil
745}
746