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