ureader_yes.go raw
1 // Copyright 2021 The Go Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style
3 // license that can be found in the LICENSE file.
4
5 // Derived from go/internal/gcimporter/ureader.go
6
7 package gcimporter
8
9 import (
10 "fmt"
11 "go/token"
12 "go/types"
13 "sort"
14
15 "golang.org/x/tools/internal/aliases"
16 "golang.org/x/tools/internal/pkgbits"
17 "golang.org/x/tools/internal/typesinternal"
18 )
19
20 // A pkgReader holds the shared state for reading a unified IR package
21 // description.
22 type pkgReader struct {
23 pkgbits.PkgDecoder
24
25 fake fakeFileSet
26
27 ctxt *types.Context
28 imports map[string]*types.Package // previously imported packages, indexed by path
29 aliases bool // create types.Alias nodes
30
31 // lazily initialized arrays corresponding to the unified IR
32 // PosBase, Pkg, and Type sections, respectively.
33 posBases []string // position bases (i.e., file names)
34 pkgs []*types.Package
35 typs []types.Type
36
37 // laterFns holds functions that need to be invoked at the end of
38 // import reading.
39 laterFns []func()
40 // laterFors is used in case of 'type A B' to ensure that B is processed before A.
41 laterFors map[types.Type]int
42
43 // ifaces holds a list of constructed Interfaces, which need to have
44 // Complete called after importing is done.
45 ifaces []*types.Interface
46 }
47
48 // later adds a function to be invoked at the end of import reading.
49 func (pr *pkgReader) later(fn func()) {
50 pr.laterFns = append(pr.laterFns, fn)
51 }
52
53 // See cmd/compile/internal/noder.derivedInfo.
54 type derivedInfo struct {
55 idx pkgbits.Index
56 }
57
58 // See cmd/compile/internal/noder.typeInfo.
59 type typeInfo struct {
60 idx pkgbits.Index
61 derived bool
62 }
63
64 func UImportData(fset *token.FileSet, imports map[string]*types.Package, data []byte, path string) (_ int, pkg *types.Package, err error) {
65 if !debug {
66 defer func() {
67 if x := recover(); x != nil {
68 err = fmt.Errorf("internal error in importing %q (%v); please report an issue", path, x)
69 }
70 }()
71 }
72
73 s := string(data)
74 input := pkgbits.NewPkgDecoder(path, s)
75 pkg = readUnifiedPackage(fset, nil, imports, input)
76 return
77 }
78
79 // laterFor adds a function to be invoked at the end of import reading, and records the type that function is finishing.
80 func (pr *pkgReader) laterFor(t types.Type, fn func()) {
81 if pr.laterFors == nil {
82 pr.laterFors = make(map[types.Type]int)
83 }
84 pr.laterFors[t] = len(pr.laterFns)
85 pr.laterFns = append(pr.laterFns, fn)
86 }
87
88 // readUnifiedPackage reads a package description from the given
89 // unified IR export data decoder.
90 func readUnifiedPackage(fset *token.FileSet, ctxt *types.Context, imports map[string]*types.Package, input pkgbits.PkgDecoder) *types.Package {
91 pr := pkgReader{
92 PkgDecoder: input,
93
94 fake: fakeFileSet{
95 fset: fset,
96 files: make(map[string]*fileInfo),
97 },
98
99 ctxt: ctxt,
100 imports: imports,
101 aliases: aliases.Enabled(),
102
103 posBases: make([]string, input.NumElems(pkgbits.RelocPosBase)),
104 pkgs: make([]*types.Package, input.NumElems(pkgbits.RelocPkg)),
105 typs: make([]types.Type, input.NumElems(pkgbits.RelocType)),
106 }
107 defer pr.fake.setLines()
108
109 r := pr.newReader(pkgbits.RelocMeta, pkgbits.PublicRootIdx, pkgbits.SyncPublic)
110 pkg := r.pkg()
111 if r.Version().Has(pkgbits.HasInit) {
112 r.Bool()
113 }
114
115 for i, n := 0, r.Len(); i < n; i++ {
116 // As if r.obj(), but avoiding the Scope.Lookup call,
117 // to avoid eager loading of imports.
118 r.Sync(pkgbits.SyncObject)
119 if r.Version().Has(pkgbits.DerivedFuncInstance) {
120 assert(!r.Bool())
121 }
122 r.p.objIdx(r.Reloc(pkgbits.RelocObj))
123 assert(r.Len() == 0)
124 }
125
126 r.Sync(pkgbits.SyncEOF)
127
128 for _, fn := range pr.laterFns {
129 fn()
130 }
131
132 for _, iface := range pr.ifaces {
133 iface.Complete()
134 }
135
136 // Imports() of pkg are all of the transitive packages that were loaded.
137 var imps []*types.Package
138 for _, imp := range pr.pkgs {
139 if imp != nil && imp != pkg {
140 imps = append(imps, imp)
141 }
142 }
143 sort.Sort(byPath(imps))
144 pkg.SetImports(imps)
145
146 pkg.MarkComplete()
147 return pkg
148 }
149
150 // A reader holds the state for reading a single unified IR element
151 // within a package.
152 type reader struct {
153 pkgbits.Decoder
154
155 p *pkgReader
156
157 dict *readerDict
158 }
159
160 // A readerDict holds the state for type parameters that parameterize
161 // the current unified IR element.
162 type readerDict struct {
163 // bounds is a slice of typeInfos corresponding to the underlying
164 // bounds of the element's type parameters.
165 bounds []typeInfo
166
167 // tparams is a slice of the constructed TypeParams for the element.
168 tparams []*types.TypeParam
169
170 // derived is a slice of types derived from tparams, which may be
171 // instantiated while reading the current element.
172 derived []derivedInfo
173 derivedTypes []types.Type // lazily instantiated from derived
174 }
175
176 func (pr *pkgReader) newReader(k pkgbits.RelocKind, idx pkgbits.Index, marker pkgbits.SyncMarker) *reader {
177 return &reader{
178 Decoder: pr.NewDecoder(k, idx, marker),
179 p: pr,
180 }
181 }
182
183 func (pr *pkgReader) tempReader(k pkgbits.RelocKind, idx pkgbits.Index, marker pkgbits.SyncMarker) *reader {
184 return &reader{
185 Decoder: pr.TempDecoder(k, idx, marker),
186 p: pr,
187 }
188 }
189
190 func (pr *pkgReader) retireReader(r *reader) {
191 pr.RetireDecoder(&r.Decoder)
192 }
193
194 // @@@ Positions
195
196 func (r *reader) pos() token.Pos {
197 r.Sync(pkgbits.SyncPos)
198 if !r.Bool() {
199 return token.NoPos
200 }
201
202 // TODO(mdempsky): Delta encoding.
203 posBase := r.posBase()
204 line := r.Uint()
205 col := r.Uint()
206 return r.p.fake.pos(posBase, int(line), int(col))
207 }
208
209 func (r *reader) posBase() string {
210 return r.p.posBaseIdx(r.Reloc(pkgbits.RelocPosBase))
211 }
212
213 func (pr *pkgReader) posBaseIdx(idx pkgbits.Index) string {
214 if b := pr.posBases[idx]; b != "" {
215 return b
216 }
217
218 var filename string
219 {
220 r := pr.tempReader(pkgbits.RelocPosBase, idx, pkgbits.SyncPosBase)
221
222 // Within types2, position bases have a lot more details (e.g.,
223 // keeping track of where //line directives appeared exactly).
224 //
225 // For go/types, we just track the file name.
226
227 filename = r.String()
228
229 if r.Bool() { // file base
230 // Was: "b = token.NewTrimmedFileBase(filename, true)"
231 } else { // line base
232 pos := r.pos()
233 line := r.Uint()
234 col := r.Uint()
235
236 // Was: "b = token.NewLineBase(pos, filename, true, line, col)"
237 _, _, _ = pos, line, col
238 }
239 pr.retireReader(r)
240 }
241 b := filename
242 pr.posBases[idx] = b
243 return b
244 }
245
246 // @@@ Packages
247
248 func (r *reader) pkg() *types.Package {
249 r.Sync(pkgbits.SyncPkg)
250 return r.p.pkgIdx(r.Reloc(pkgbits.RelocPkg))
251 }
252
253 func (pr *pkgReader) pkgIdx(idx pkgbits.Index) *types.Package {
254 // TODO(mdempsky): Consider using some non-nil pointer to indicate
255 // the universe scope, so we don't need to keep re-reading it.
256 if pkg := pr.pkgs[idx]; pkg != nil {
257 return pkg
258 }
259
260 pkg := pr.newReader(pkgbits.RelocPkg, idx, pkgbits.SyncPkgDef).doPkg()
261 pr.pkgs[idx] = pkg
262 return pkg
263 }
264
265 func (r *reader) doPkg() *types.Package {
266 path := r.String()
267 switch path {
268 // cmd/compile emits path="main" for main packages because
269 // that's the linker symbol prefix it used; but we need
270 // the package's path as it would be reported by go list,
271 // hence "main" below.
272 // See test at go/packages.TestMainPackagePathInModeTypes.
273 case "", "main":
274 path = r.p.PkgPath()
275 case "builtin":
276 return nil // universe
277 case "unsafe":
278 return types.Unsafe
279 }
280
281 if pkg := r.p.imports[path]; pkg != nil {
282 return pkg
283 }
284
285 name := r.String()
286
287 pkg := types.NewPackage(path, name)
288 r.p.imports[path] = pkg
289
290 return pkg
291 }
292
293 // @@@ Types
294
295 func (r *reader) typ() types.Type {
296 return r.p.typIdx(r.typInfo(), r.dict)
297 }
298
299 func (r *reader) typInfo() typeInfo {
300 r.Sync(pkgbits.SyncType)
301 if r.Bool() {
302 return typeInfo{idx: pkgbits.Index(r.Len()), derived: true}
303 }
304 return typeInfo{idx: r.Reloc(pkgbits.RelocType), derived: false}
305 }
306
307 func (pr *pkgReader) typIdx(info typeInfo, dict *readerDict) types.Type {
308 idx := info.idx
309 var where *types.Type
310 if info.derived {
311 where = &dict.derivedTypes[idx]
312 idx = dict.derived[idx].idx
313 } else {
314 where = &pr.typs[idx]
315 }
316
317 if typ := *where; typ != nil {
318 return typ
319 }
320
321 var typ types.Type
322 {
323 r := pr.tempReader(pkgbits.RelocType, idx, pkgbits.SyncTypeIdx)
324 r.dict = dict
325
326 typ = r.doTyp()
327 assert(typ != nil)
328 pr.retireReader(r)
329 }
330 // See comment in pkgReader.typIdx explaining how this happens.
331 if prev := *where; prev != nil {
332 return prev
333 }
334
335 *where = typ
336 return typ
337 }
338
339 func (r *reader) doTyp() (res types.Type) {
340 switch tag := pkgbits.CodeType(r.Code(pkgbits.SyncType)); tag {
341 default:
342 errorf("unhandled type tag: %v", tag)
343 panic("unreachable")
344
345 case pkgbits.TypeBasic:
346 return types.Typ[r.Len()]
347
348 case pkgbits.TypeNamed:
349 obj, targs := r.obj()
350 name := obj.(*types.TypeName)
351 if len(targs) != 0 {
352 t, _ := types.Instantiate(r.p.ctxt, name.Type(), targs, false)
353 return t
354 }
355 return name.Type()
356
357 case pkgbits.TypeTypeParam:
358 return r.dict.tparams[r.Len()]
359
360 case pkgbits.TypeArray:
361 len := int64(r.Uint64())
362 return types.NewArray(r.typ(), len)
363 case pkgbits.TypeChan:
364 dir := types.ChanDir(r.Len())
365 return types.NewChan(dir, r.typ())
366 case pkgbits.TypeMap:
367 return types.NewMap(r.typ(), r.typ())
368 case pkgbits.TypePointer:
369 return types.NewPointer(r.typ())
370 case pkgbits.TypeSignature:
371 return r.signature(nil, nil, nil)
372 case pkgbits.TypeSlice:
373 return types.NewSlice(r.typ())
374 case pkgbits.TypeStruct:
375 return r.structType()
376 case pkgbits.TypeInterface:
377 return r.interfaceType()
378 case pkgbits.TypeUnion:
379 return r.unionType()
380 }
381 }
382
383 func (r *reader) structType() *types.Struct {
384 fields := make([]*types.Var, r.Len())
385 var tags []string
386 for i := range fields {
387 pos := r.pos()
388 pkg, name := r.selector()
389 ftyp := r.typ()
390 tag := r.String()
391 embedded := r.Bool()
392
393 fields[i] = types.NewField(pos, pkg, name, ftyp, embedded)
394 if tag != "" {
395 for len(tags) < i {
396 tags = append(tags, "")
397 }
398 tags = append(tags, tag)
399 }
400 }
401 return types.NewStruct(fields, tags)
402 }
403
404 func (r *reader) unionType() *types.Union {
405 terms := make([]*types.Term, r.Len())
406 for i := range terms {
407 terms[i] = types.NewTerm(r.Bool(), r.typ())
408 }
409 return types.NewUnion(terms)
410 }
411
412 func (r *reader) interfaceType() *types.Interface {
413 methods := make([]*types.Func, r.Len())
414 embeddeds := make([]types.Type, r.Len())
415 implicit := len(methods) == 0 && len(embeddeds) == 1 && r.Bool()
416
417 for i := range methods {
418 pos := r.pos()
419 pkg, name := r.selector()
420 mtyp := r.signature(nil, nil, nil)
421 methods[i] = types.NewFunc(pos, pkg, name, mtyp)
422 }
423
424 for i := range embeddeds {
425 embeddeds[i] = r.typ()
426 }
427
428 iface := types.NewInterfaceType(methods, embeddeds)
429 if implicit {
430 iface.MarkImplicit()
431 }
432
433 // We need to call iface.Complete(), but if there are any embedded
434 // defined types, then we may not have set their underlying
435 // interface type yet. So we need to defer calling Complete until
436 // after we've called SetUnderlying everywhere.
437 //
438 // TODO(mdempsky): After CL 424876 lands, it should be safe to call
439 // iface.Complete() immediately.
440 r.p.ifaces = append(r.p.ifaces, iface)
441
442 return iface
443 }
444
445 func (r *reader) signature(recv *types.Var, rtparams, tparams []*types.TypeParam) *types.Signature {
446 r.Sync(pkgbits.SyncSignature)
447
448 params := r.params()
449 results := r.params()
450 variadic := r.Bool()
451
452 return types.NewSignatureType(recv, rtparams, tparams, params, results, variadic)
453 }
454
455 func (r *reader) params() *types.Tuple {
456 r.Sync(pkgbits.SyncParams)
457
458 params := make([]*types.Var, r.Len())
459 for i := range params {
460 params[i] = r.param()
461 }
462
463 return types.NewTuple(params...)
464 }
465
466 func (r *reader) param() *types.Var {
467 r.Sync(pkgbits.SyncParam)
468
469 pos := r.pos()
470 pkg, name := r.localIdent()
471 typ := r.typ()
472
473 return types.NewParam(pos, pkg, name, typ)
474 }
475
476 // @@@ Objects
477
478 func (r *reader) obj() (types.Object, []types.Type) {
479 r.Sync(pkgbits.SyncObject)
480
481 if r.Version().Has(pkgbits.DerivedFuncInstance) {
482 assert(!r.Bool())
483 }
484
485 pkg, name := r.p.objIdx(r.Reloc(pkgbits.RelocObj))
486 obj := pkgScope(pkg).Lookup(name)
487
488 targs := make([]types.Type, r.Len())
489 for i := range targs {
490 targs[i] = r.typ()
491 }
492
493 return obj, targs
494 }
495
496 func (pr *pkgReader) objIdx(idx pkgbits.Index) (*types.Package, string) {
497
498 var objPkg *types.Package
499 var objName string
500 var tag pkgbits.CodeObj
501 {
502 rname := pr.tempReader(pkgbits.RelocName, idx, pkgbits.SyncObject1)
503
504 objPkg, objName = rname.qualifiedIdent()
505 assert(objName != "")
506
507 tag = pkgbits.CodeObj(rname.Code(pkgbits.SyncCodeObj))
508 pr.retireReader(rname)
509 }
510
511 if tag == pkgbits.ObjStub {
512 assert(objPkg == nil || objPkg == types.Unsafe)
513 return objPkg, objName
514 }
515
516 // Ignore local types promoted to global scope (#55110).
517 if _, suffix := splitVargenSuffix(objName); suffix != "" {
518 return objPkg, objName
519 }
520
521 if objPkg.Scope().Lookup(objName) == nil {
522 dict := pr.objDictIdx(idx)
523
524 r := pr.newReader(pkgbits.RelocObj, idx, pkgbits.SyncObject1)
525 r.dict = dict
526
527 declare := func(obj types.Object) {
528 objPkg.Scope().Insert(obj)
529 }
530
531 switch tag {
532 default:
533 panic("weird")
534
535 case pkgbits.ObjAlias:
536 pos := r.pos()
537 var tparams []*types.TypeParam
538 if r.Version().Has(pkgbits.AliasTypeParamNames) {
539 tparams = r.typeParamNames()
540 }
541 typ := r.typ()
542 declare(aliases.NewAlias(r.p.aliases, pos, objPkg, objName, typ, tparams))
543
544 case pkgbits.ObjConst:
545 pos := r.pos()
546 typ := r.typ()
547 val := r.Value()
548 declare(types.NewConst(pos, objPkg, objName, typ, val))
549
550 case pkgbits.ObjFunc:
551 pos := r.pos()
552 tparams := r.typeParamNames()
553 sig := r.signature(nil, nil, tparams)
554 declare(types.NewFunc(pos, objPkg, objName, sig))
555
556 case pkgbits.ObjType:
557 pos := r.pos()
558
559 obj := types.NewTypeName(pos, objPkg, objName, nil)
560 named := types.NewNamed(obj, nil, nil)
561 declare(obj)
562
563 named.SetTypeParams(r.typeParamNames())
564
565 setUnderlying := func(underlying types.Type) {
566 // If the underlying type is an interface, we need to
567 // duplicate its methods so we can replace the receiver
568 // parameter's type (#49906).
569 if iface, ok := types.Unalias(underlying).(*types.Interface); ok && iface.NumExplicitMethods() != 0 {
570 methods := make([]*types.Func, iface.NumExplicitMethods())
571 for i := range methods {
572 fn := iface.ExplicitMethod(i)
573 sig := fn.Type().(*types.Signature)
574
575 recv := types.NewVar(fn.Pos(), fn.Pkg(), "", named)
576 typesinternal.SetVarKind(recv, typesinternal.RecvVar)
577 methods[i] = types.NewFunc(fn.Pos(), fn.Pkg(), fn.Name(), types.NewSignatureType(recv, nil, nil, sig.Params(), sig.Results(), sig.Variadic()))
578 }
579
580 embeds := make([]types.Type, iface.NumEmbeddeds())
581 for i := range embeds {
582 embeds[i] = iface.EmbeddedType(i)
583 }
584
585 newIface := types.NewInterfaceType(methods, embeds)
586 r.p.ifaces = append(r.p.ifaces, newIface)
587 underlying = newIface
588 }
589
590 named.SetUnderlying(underlying)
591 }
592
593 // Since go.dev/cl/455279, we can assume rhs.Underlying() will
594 // always be non-nil. However, to temporarily support users of
595 // older snapshot releases, we continue to fallback to the old
596 // behavior for now.
597 //
598 // TODO(mdempsky): Remove fallback code and simplify after
599 // allowing time for snapshot users to upgrade.
600 rhs := r.typ()
601 if underlying := rhs.Underlying(); underlying != nil {
602 setUnderlying(underlying)
603 } else {
604 pk := r.p
605 pk.laterFor(named, func() {
606 // First be sure that the rhs is initialized, if it needs to be initialized.
607 delete(pk.laterFors, named) // prevent cycles
608 if i, ok := pk.laterFors[rhs]; ok {
609 f := pk.laterFns[i]
610 pk.laterFns[i] = func() {} // function is running now, so replace it with a no-op
611 f() // initialize RHS
612 }
613 setUnderlying(rhs.Underlying())
614 })
615 }
616
617 for i, n := 0, r.Len(); i < n; i++ {
618 named.AddMethod(r.method())
619 }
620
621 case pkgbits.ObjVar:
622 pos := r.pos()
623 typ := r.typ()
624 v := types.NewVar(pos, objPkg, objName, typ)
625 typesinternal.SetVarKind(v, typesinternal.PackageVar)
626 declare(v)
627 }
628 }
629
630 return objPkg, objName
631 }
632
633 func (pr *pkgReader) objDictIdx(idx pkgbits.Index) *readerDict {
634
635 var dict readerDict
636
637 {
638 r := pr.tempReader(pkgbits.RelocObjDict, idx, pkgbits.SyncObject1)
639 if implicits := r.Len(); implicits != 0 {
640 errorf("unexpected object with %v implicit type parameter(s)", implicits)
641 }
642
643 dict.bounds = make([]typeInfo, r.Len())
644 for i := range dict.bounds {
645 dict.bounds[i] = r.typInfo()
646 }
647
648 dict.derived = make([]derivedInfo, r.Len())
649 dict.derivedTypes = make([]types.Type, len(dict.derived))
650 for i := range dict.derived {
651 dict.derived[i] = derivedInfo{idx: r.Reloc(pkgbits.RelocType)}
652 if r.Version().Has(pkgbits.DerivedInfoNeeded) {
653 assert(!r.Bool())
654 }
655 }
656
657 pr.retireReader(r)
658 }
659 // function references follow, but reader doesn't need those
660
661 return &dict
662 }
663
664 func (r *reader) typeParamNames() []*types.TypeParam {
665 r.Sync(pkgbits.SyncTypeParamNames)
666
667 // Note: This code assumes it only processes objects without
668 // implement type parameters. This is currently fine, because
669 // reader is only used to read in exported declarations, which are
670 // always package scoped.
671
672 if len(r.dict.bounds) == 0 {
673 return nil
674 }
675
676 // Careful: Type parameter lists may have cycles. To allow for this,
677 // we construct the type parameter list in two passes: first we
678 // create all the TypeNames and TypeParams, then we construct and
679 // set the bound type.
680
681 r.dict.tparams = make([]*types.TypeParam, len(r.dict.bounds))
682 for i := range r.dict.bounds {
683 pos := r.pos()
684 pkg, name := r.localIdent()
685
686 tname := types.NewTypeName(pos, pkg, name, nil)
687 r.dict.tparams[i] = types.NewTypeParam(tname, nil)
688 }
689
690 typs := make([]types.Type, len(r.dict.bounds))
691 for i, bound := range r.dict.bounds {
692 typs[i] = r.p.typIdx(bound, r.dict)
693 }
694
695 // TODO(mdempsky): This is subtle, elaborate further.
696 //
697 // We have to save tparams outside of the closure, because
698 // typeParamNames() can be called multiple times with the same
699 // dictionary instance.
700 //
701 // Also, this needs to happen later to make sure SetUnderlying has
702 // been called.
703 //
704 // TODO(mdempsky): Is it safe to have a single "later" slice or do
705 // we need to have multiple passes? See comments on CL 386002 and
706 // go.dev/issue/52104.
707 tparams := r.dict.tparams
708 r.p.later(func() {
709 for i, typ := range typs {
710 tparams[i].SetConstraint(typ)
711 }
712 })
713
714 return r.dict.tparams
715 }
716
717 func (r *reader) method() *types.Func {
718 r.Sync(pkgbits.SyncMethod)
719 pos := r.pos()
720 pkg, name := r.selector()
721
722 rparams := r.typeParamNames()
723 sig := r.signature(r.param(), rparams, nil)
724
725 _ = r.pos() // TODO(mdempsky): Remove; this is a hacker for linker.go.
726 return types.NewFunc(pos, pkg, name, sig)
727 }
728
729 func (r *reader) qualifiedIdent() (*types.Package, string) { return r.ident(pkgbits.SyncSym) }
730 func (r *reader) localIdent() (*types.Package, string) { return r.ident(pkgbits.SyncLocalIdent) }
731 func (r *reader) selector() (*types.Package, string) { return r.ident(pkgbits.SyncSelector) }
732
733 func (r *reader) ident(marker pkgbits.SyncMarker) (*types.Package, string) {
734 r.Sync(marker)
735 return r.pkg(), r.String()
736 }
737
738 // pkgScope returns pkg.Scope().
739 // If pkg is nil, it returns types.Universe instead.
740 //
741 // TODO(mdempsky): Remove after x/tools can depend on Go 1.19.
742 func pkgScope(pkg *types.Package) *types.Scope {
743 if pkg != nil {
744 return pkg.Scope()
745 }
746 return types.Universe
747 }
748
749 // See cmd/compile/internal/types.SplitVargenSuffix.
750 func splitVargenSuffix(name string) (base, suffix string) {
751 i := len(name)
752 for i > 0 && name[i-1] >= '0' && name[i-1] <= '9' {
753 i--
754 }
755 const dot = "ยท"
756 if i >= len(dot) && name[i-len(dot):i] == dot {
757 i -= len(dot)
758 return name[:i], name[i:]
759 }
760 return name, ""
761 }
762