From 6cdb87ab17bbe64bad97b20fdf2476618d30e292 Mon Sep 17 00:00:00 2001 From: Randy Reddig Date: Mon, 20 Jan 2025 08:56:00 -0800 Subject: [PATCH 01/15] wit/bindgen: log errors in testdata tests --- wit/bindgen/testdata_test.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/wit/bindgen/testdata_test.go b/wit/bindgen/testdata_test.go index 9cb53774..05c89471 100644 --- a/wit/bindgen/testdata_test.go +++ b/wit/bindgen/testdata_test.go @@ -21,6 +21,7 @@ import ( "go.bytecodealliance.org/internal/go/gen" "go.bytecodealliance.org/internal/relpath" "go.bytecodealliance.org/wit" + "go.bytecodealliance.org/wit/logging" ) var writeGoFiles = flag.Bool("write", false, "write generated Go files") @@ -108,6 +109,7 @@ func validateGeneratedGo(t *testing.T, res *wit.Resolve, origin string) { GeneratedBy("test"), PackageRoot(pkgPath), Versioned(true), + Logger(logging.NewLogger(os.Stderr, logging.LevelWarn)), ) if err != nil { t.Error(err) From c8cf34ab01399e4e465c8529091e2f72eddf5740 Mon Sep 17 00:00:00 2001 From: Randy Reddig Date: Mon, 20 Jan 2025 08:56:44 -0800 Subject: [PATCH 02/15] wit/bindgen: log WASI exit code errors These were previously swallowed. --- wit/bindgen/generator.go | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/wit/bindgen/generator.go b/wit/bindgen/generator.go index 553b42f4..f8e91504 100644 --- a/wit/bindgen/generator.go +++ b/wit/bindgen/generator.go @@ -20,6 +20,7 @@ import ( "testing/fstest" "time" + "github.com/tetratelabs/wazero/sys" "go.bytecodealliance.org/cm" "go.bytecodealliance.org/internal/codec" "go.bytecodealliance.org/internal/go/gen" @@ -2370,6 +2371,7 @@ func (g *generator) newPackage(w *wit.World, i *wit.Interface, name string) (*ge } content, err := g.componentEmbed(witText) if err != nil { + g.opts.logger.Errorf("%v", err) // return nil, err } componentType := &wasm.CustomSection{ @@ -2406,8 +2408,12 @@ func (g *generator) componentEmbed(witData string) ([]byte, error) { }, } stdout := &bytes.Buffer{} - err := g.wasmTools.Run(ctx, nil, stdout, nil, fsMap, args...) + stderr := &bytes.Buffer{} + err := g.wasmTools.Run(ctx, nil, stdout, stderr, fsMap, args...) if err != nil { + if _, ok := err.(*sys.ExitError); ok { + return nil, fmt.Errorf("wasm-tools: %s", stderr.String()) + } return nil, fmt.Errorf("wasm-tools: %w", err) } return stdout.Bytes(), err From fe9678535dc9cfe599e3da158e9d708eeb6e8283 Mon Sep 17 00:00:00 2001 From: Randy Reddig Date: Mon, 20 Jan 2025 08:57:03 -0800 Subject: [PATCH 03/15] wit/bindgen: use correct directory paths for Wazero --- wit/bindgen/generator.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/wit/bindgen/generator.go b/wit/bindgen/generator.go index f8e91504..b0f3d581 100644 --- a/wit/bindgen/generator.go +++ b/wit/bindgen/generator.go @@ -2400,10 +2400,10 @@ func (g *generator) componentEmbed(witData string) ([]byte, error) { defer cancel() // TODO: --all-features? - filename := "./component.wit" + filename := "component.wit" args := []string{"component", "embed", "--only-custom", filename} fsMap := map[string]fs.FS{ - ".": fstest.MapFS{ + "": fstest.MapFS{ filename: &fstest.MapFile{Data: []byte(witData)}, }, } From 7df88da9136dd1b41ac8bdfe4a3232a299532e38 Mon Sep 17 00:00:00 2001 From: Randy Reddig Date: Mon, 20 Jan 2025 08:57:21 -0800 Subject: [PATCH 04/15] testdata/issues: add test case for #281 --- testdata/issues/issue281.wit | 11 ++++ testdata/issues/issue281.wit.json | 53 ++++++++++++++++++++ testdata/issues/issue281.wit.json.golden.wit | 11 ++++ 3 files changed, 75 insertions(+) create mode 100644 testdata/issues/issue281.wit create mode 100644 testdata/issues/issue281.wit.json create mode 100644 testdata/issues/issue281.wit.json.golden.wit diff --git a/testdata/issues/issue281.wit b/testdata/issues/issue281.wit new file mode 100644 index 00000000..e818497e --- /dev/null +++ b/testdata/issues/issue281.wit @@ -0,0 +1,11 @@ +package repro:repro@0.1.0; + +world imports { + import wasi:http/types@0.2.1; +} + +package wasi:http@0.2.1 { + interface types { + type t = u8; + } +} diff --git a/testdata/issues/issue281.wit.json b/testdata/issues/issue281.wit.json new file mode 100644 index 00000000..f758be28 --- /dev/null +++ b/testdata/issues/issue281.wit.json @@ -0,0 +1,53 @@ +{ + "worlds": [ + { + "name": "imports", + "imports": { + "interface-0": { + "interface": { + "id": 0 + } + } + }, + "exports": {}, + "package": 1 + } + ], + "interfaces": [ + { + "name": "types", + "types": { + "t": 0 + }, + "functions": {}, + "package": 0 + } + ], + "types": [ + { + "name": "t", + "kind": { + "type": "u8" + }, + "owner": { + "interface": 0 + } + } + ], + "packages": [ + { + "name": "wasi:http@0.2.1", + "interfaces": { + "types": 0 + }, + "worlds": {} + }, + { + "name": "repro:repro@0.1.0", + "interfaces": {}, + "worlds": { + "imports": 0 + } + } + ] +} \ No newline at end of file diff --git a/testdata/issues/issue281.wit.json.golden.wit b/testdata/issues/issue281.wit.json.golden.wit new file mode 100644 index 00000000..3e859eef --- /dev/null +++ b/testdata/issues/issue281.wit.json.golden.wit @@ -0,0 +1,11 @@ +package repro:repro@0.1.0; + +world imports { + import wasi:http/types@0.2.1; +} + +package wasi:http@0.2.1 { + interface types { + type t = u8; + } +} From f9c2af7d6639ecad3e90d67ec70c582ba7471669 Mon Sep 17 00:00:00 2001 From: Randy Reddig Date: Mon, 20 Jan 2025 09:11:22 -0800 Subject: [PATCH 05/15] wit/bindgen: synthesize worlds with version 1.0.0 Fixes #281. --- wit/bindgen/generator.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/wit/bindgen/generator.go b/wit/bindgen/generator.go index b0f3d581..0f04a859 100644 --- a/wit/bindgen/generator.go +++ b/wit/bindgen/generator.go @@ -20,6 +20,7 @@ import ( "testing/fstest" "time" + "github.com/coreos/go-semver/semver" "github.com/tetratelabs/wazero/sys" "go.bytecodealliance.org/cm" "go.bytecodealliance.org/internal/codec" @@ -2423,6 +2424,7 @@ func synthesizeWorld(r *wit.Resolve, w *wit.World, name string) (*wit.Resolve, * p := &wit.Package{} p.Name.Namespace = "go" p.Name.Package = "bindgen" + p.Name.Version = &semver.Version{Major: 1} w = w.Clone() w.Name = name From 3bd660ded423e77f1d92219e7f2ee4f3bc408af9 Mon Sep 17 00:00:00 2001 From: Randy Reddig Date: Mon, 20 Jan 2025 17:31:42 -0800 Subject: [PATCH 06/15] testdata/issues: correct package name --- testdata/issues/issue281.wit | 2 +- testdata/issues/issue281.wit.json | 2 +- testdata/issues/issue281.wit.json.golden.wit | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/testdata/issues/issue281.wit b/testdata/issues/issue281.wit index e818497e..bee7fc76 100644 --- a/testdata/issues/issue281.wit +++ b/testdata/issues/issue281.wit @@ -1,4 +1,4 @@ -package repro:repro@0.1.0; +package issues:issue281@0.1.0; world imports { import wasi:http/types@0.2.1; diff --git a/testdata/issues/issue281.wit.json b/testdata/issues/issue281.wit.json index f758be28..e6bb6a57 100644 --- a/testdata/issues/issue281.wit.json +++ b/testdata/issues/issue281.wit.json @@ -43,7 +43,7 @@ "worlds": {} }, { - "name": "repro:repro@0.1.0", + "name": "issues:issue281@0.1.0", "interfaces": {}, "worlds": { "imports": 0 diff --git a/testdata/issues/issue281.wit.json.golden.wit b/testdata/issues/issue281.wit.json.golden.wit index 3e859eef..dab07157 100644 --- a/testdata/issues/issue281.wit.json.golden.wit +++ b/testdata/issues/issue281.wit.json.golden.wit @@ -1,4 +1,4 @@ -package repro:repro@0.1.0; +package issues:issue281@0.1.0; world imports { import wasi:http/types@0.2.1; From 6c9597e6ed4f2763703d411a64f1f5943bbb4620 Mon Sep 17 00:00:00 2001 From: Randy Reddig Date: Mon, 20 Jan 2025 17:40:14 -0800 Subject: [PATCH 07/15] wit/bindgen: log generated WIT that results in an error --- wit/bindgen/generator.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/wit/bindgen/generator.go b/wit/bindgen/generator.go index 0f04a859..47e3bd31 100644 --- a/wit/bindgen/generator.go +++ b/wit/bindgen/generator.go @@ -2372,8 +2372,9 @@ func (g *generator) newPackage(w *wit.World, i *wit.Interface, name string) (*ge } content, err := g.componentEmbed(witText) if err != nil { - g.opts.logger.Errorf("%v", err) - // return nil, err + // g.opts.logger.Errorf("%v", err) + g.opts.logger.Errorf("WIT:\n%s\n\n", witText) + return nil, err } componentType := &wasm.CustomSection{ Name: "component-type:" + worldName, From 2d452fab3246924f3a44d97ef51a1c3ac8f60719 Mon Sep 17 00:00:00 2001 From: Randy Reddig Date: Mon, 20 Jan 2025 17:41:07 -0800 Subject: [PATCH 08/15] wit: escape WIT keywords in WIT package and world identifier strings --- wit/ident.go | 8 ++++---- wit/wit.go | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/wit/ident.go b/wit/ident.go index 1a7a902c..3397b02d 100644 --- a/wit/ident.go +++ b/wit/ident.go @@ -68,15 +68,15 @@ func (id *Ident) String() string { return id.UnversionedString() } if id.Extension == "" { - return id.Namespace + ":" + id.Package + "@" + id.Version.String() + return escape(id.Namespace) + ":" + escape(id.Package) + "@" + id.Version.String() } - return id.Namespace + ":" + id.Package + "/" + id.Extension + "@" + id.Version.String() + return escape(id.Namespace) + ":" + escape(id.Package) + "/" + escape(id.Extension) + "@" + id.Version.String() } // UnversionedString returns a string representation of an [Ident] without version information. func (id *Ident) UnversionedString() string { if id.Extension == "" { - return id.Namespace + ":" + id.Package + return escape(id.Namespace) + ":" + escape(id.Package) } - return id.Namespace + ":" + id.Package + "/" + id.Extension + return escape(id.Namespace) + ":" + escape(id.Package) + "/" + escape(id.Extension) } diff --git a/wit/wit.go b/wit/wit.go index b9687b3b..f1657e52 100644 --- a/wit/wit.go +++ b/wit/wit.go @@ -578,7 +578,7 @@ func relativeName(o TypeOwner, p *Package) string { return "" } qualifiedName := op.Name - qualifiedName.Package += "/" + name + qualifiedName.Package += "/" + escape(name) return qualifiedName.String() } From 712ef3a0b05a601f64d53b2abc25d10a0f34cbe7 Mon Sep 17 00:00:00 2001 From: Randy Reddig Date: Mon, 20 Jan 2025 17:43:37 -0800 Subject: [PATCH 09/15] wit/bindgen: use preexisting WIT package for synthetic world This allows the use of use statements inside of inline interface imports. --- wit/bindgen/generator.go | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/wit/bindgen/generator.go b/wit/bindgen/generator.go index 47e3bd31..63f78020 100644 --- a/wit/bindgen/generator.go +++ b/wit/bindgen/generator.go @@ -20,7 +20,6 @@ import ( "testing/fstest" "time" - "github.com/coreos/go-semver/semver" "github.com/tetratelabs/wazero/sys" "go.bytecodealliance.org/cm" "go.bytecodealliance.org/internal/codec" @@ -2366,6 +2365,7 @@ func (g *generator) newPackage(w *wit.World, i *wit.Interface, name string) (*ge // Generate wasm file res, world := synthesizeWorld(g.res, w, worldName) witText := res.WIT(wit.Filter(world, i), "") + world.Package.Worlds.Delete(worldName) // Undo mutation if g.opts.generateWIT { witFile := g.witFileFor(owner) witFile.WriteString(witText) @@ -2394,7 +2394,7 @@ func (g *generator) newPackage(w *wit.World, i *wit.Interface, name string) (*ge return pkg, nil } -var replacer = strings.NewReplacer("/", "-", ":", "-", "@", "-v", ".", "") +var replacer = strings.NewReplacer("/", "-", ":", "-", "@", "-v", ".", "", "%", "") // componentEmbed runs generated WIT through wasm-tools to generate a wasm file with a component-type custom section. func (g *generator) componentEmbed(witData string) ([]byte, error) { @@ -2422,20 +2422,13 @@ func (g *generator) componentEmbed(witData string) ([]byte, error) { } func synthesizeWorld(r *wit.Resolve, w *wit.World, name string) (*wit.Resolve, *wit.World) { - p := &wit.Package{} - p.Name.Namespace = "go" - p.Name.Package = "bindgen" - p.Name.Version = &semver.Version{Major: 1} - w = w.Clone() w.Name = name w.Docs = wit.Docs{} - w.Package = p - p.Worlds.Set(name, w) + w.Package.Worlds.Set(name, w) r = r.Clone() r.Worlds = append(r.Worlds, w) - r.Packages = append(r.Packages, p) return r, w } From 245be17f185c1e1c82828afa17a03db52c9d776f Mon Sep 17 00:00:00 2001 From: Randy Reddig Date: Mon, 20 Jan 2025 17:51:02 -0800 Subject: [PATCH 10/15] wit/bindgen: remove commented-out log line --- wit/bindgen/generator.go | 1 - 1 file changed, 1 deletion(-) diff --git a/wit/bindgen/generator.go b/wit/bindgen/generator.go index 63f78020..19d659f6 100644 --- a/wit/bindgen/generator.go +++ b/wit/bindgen/generator.go @@ -2372,7 +2372,6 @@ func (g *generator) newPackage(w *wit.World, i *wit.Interface, name string) (*ge } content, err := g.componentEmbed(witText) if err != nil { - // g.opts.logger.Errorf("%v", err) g.opts.logger.Errorf("WIT:\n%s\n\n", witText) return nil, err } From 1ffcd37a111e8a277e2c41992d60e8a157bc3dc7 Mon Sep 17 00:00:00 2001 From: Randy Reddig Date: Mon, 20 Jan 2025 17:53:13 -0800 Subject: [PATCH 11/15] CHANGELOG: note fix to #281 --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1509f5c1..325d7319 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), ### Fixed +- [#281](https://github.com/bytecodealliance/go-modules/issues/281): errors from internal `wasm-tools` calls are no longer silently ignored. This required fixing a number of related issues, including synthetic world packages for Component Model metadata generation, WIT generation, and WIT keyword escaping in WIT package or interface names. - [#284](https://github.com/bytecodealliance/go-modules/issues/284): do not use `bool` for `variant` or `result` GC shapes. TinyGo returns `result` and `variant` values with `bool` as 0 or 1, which breaks the memory representation of tagged unions (variants). ## [v0.5.0] — 2024-12-14 From ec2fe9ea19231fae43d87dd53bb56506af4089ff Mon Sep 17 00:00:00 2001 From: Randy Reddig Date: Mon, 27 Jan 2025 08:23:35 -0800 Subject: [PATCH 12/15] wit: handle % in WIT package, world, and interface identfiers --- wit/ident.go | 1 + wit/ident_test.go | 6 ++++++ 2 files changed, 7 insertions(+) diff --git a/wit/ident.go b/wit/ident.go index 3397b02d..8c750a64 100644 --- a/wit/ident.go +++ b/wit/ident.go @@ -34,6 +34,7 @@ type Ident struct { // returning any errors encountered. The resulting Ident // may not be valid. func ParseIdent(s string) (Ident, error) { + s = strings.ReplaceAll(s, "%", "") var id Ident name, ver, hasVer := strings.Cut(s, "@") base, ext, hasExt := strings.Cut(name, "/") diff --git a/wit/ident_test.go b/wit/ident_test.go index 7eff822d..1c59fb61 100644 --- a/wit/ident_test.go +++ b/wit/ident_test.go @@ -18,6 +18,12 @@ func TestIdent(t *testing.T) { {"wasi:io/streams", Ident{Namespace: "wasi", Package: "io", Extension: "streams"}, false}, {"wasi:io/streams@0.2.0", Ident{Namespace: "wasi", Package: "io", Extension: "streams", Version: semver.New("0.2.0")}, false}, + // Escaping + {"%use:%own", Ident{Namespace: "use", Package: "own"}, false}, + {"%use:%own@0.2.0", Ident{Namespace: "use", Package: "own", Version: semver.New("0.2.0")}, false}, + {"%use:%own/%type", Ident{Namespace: "use", Package: "own", Extension: "type"}, false}, + {"%use:%own/%type@0.2.0", Ident{Namespace: "use", Package: "own", Extension: "type", Version: semver.New("0.2.0")}, false}, + // Errors {"", Ident{}, true}, {":", Ident{}, true}, From 4bf4de4951f60a40138061bacf45c44008401649 Mon Sep 17 00:00:00 2001 From: Randy Reddig Date: Mon, 27 Jan 2025 09:42:08 -0800 Subject: [PATCH 13/15] wit: validate WIT identifiers --- wit/ident.go | 70 ++++++++++++++++++++++++++++++++++++++++++++--- wit/ident_test.go | 1 + 2 files changed, 67 insertions(+), 4 deletions(-) diff --git a/wit/ident.go b/wit/ident.go index 8c750a64..13c894ae 100644 --- a/wit/ident.go +++ b/wit/ident.go @@ -2,6 +2,7 @@ package wit import ( "errors" + "strconv" "strings" "github.com/coreos/go-semver/semver" @@ -34,11 +35,8 @@ type Ident struct { // returning any errors encountered. The resulting Ident // may not be valid. func ParseIdent(s string) (Ident, error) { - s = strings.ReplaceAll(s, "%", "") var id Ident name, ver, hasVer := strings.Cut(s, "@") - base, ext, hasExt := strings.Cut(name, "/") - id.Namespace, id.Package, _ = strings.Cut(base, ":") if hasVer { var err error id.Version, err = semver.NewVersion(ver) @@ -46,12 +44,23 @@ func ParseIdent(s string) (Ident, error) { return id, err } } + base, ext, hasExt := strings.Cut(name, "/") + ns, pkg, _ := strings.Cut(base, ":") + id.Namespace = trimPercent(ns) + id.Package = trimPercent(pkg) if hasExt { - id.Extension = ext + id.Extension = trimPercent(ext) } return id, id.Validate() } +func trimPercent(s string) string { + if len(s) > 0 && s[0] == '%' { + return s[1:] + } + return s +} + // Validate validates id, returning any errors. func (id *Ident) Validate() error { switch { @@ -60,6 +69,59 @@ func (id *Ident) Validate() error { case id.Package == "": return errors.New("missing package name") } + if err := validateName(id.Namespace); err != nil { + return err + } + if err := validateName(id.Package); err != nil { + return err + } + return validateName(id.Extension) +} + +func validateName(s string) error { + if len(s) == 0 { + return nil + } + var prev rune + for _, c := range s { + switch { + case c >= 'a' && c <= 'z': + switch { + case prev >= 'A' && prev <= 'Z': + return errors.New("invalid character " + strconv.Quote(string(c))) + } + case c >= 'A' && c <= 'Z': + switch { + case prev == 0: // start of string + case prev >= 'A' && prev <= 'Z': + case prev >= '0' && prev <= '9': + case prev == '-': + default: + return errors.New("invalid character " + strconv.Quote(string(c))) + } + case c >= '0' && c <= '9': + switch { + case prev >= 'a' && prev <= 'z': + case prev >= 'A' && prev <= 'Z': + case prev >= '0' && prev <= '9': + default: + return errors.New("invalid character " + strconv.Quote(string(c))) + } + case c == '-': + switch { + case prev == 0: // start of string + return errors.New("invalid leading -") + case prev == '-': + return errors.New("invalid double --") + } + default: + return errors.New("invalid character " + strconv.Quote(string(c))) + } + prev = c + } + if prev == '-' { + return errors.New("invalid trailing -") + } return nil } diff --git a/wit/ident_test.go b/wit/ident_test.go index 1c59fb61..c44355b9 100644 --- a/wit/ident_test.go +++ b/wit/ident_test.go @@ -34,6 +34,7 @@ func TestIdent(t *testing.T) { {"wasi:/", Ident{}, true}, {"wasi:clocks@", Ident{}, true}, {"wasi:clocks/wall-clock@", Ident{}, true}, + {"foo%:bar%baz", Ident{Namespace: "foo%", Package: "bar%baz"}, true}, } for _, tt := range tests { t.Run(tt.s, func(t *testing.T) { From 5b1807a501d22041001784bd2bec5c4161d6e17c Mon Sep 17 00:00:00 2001 From: Randy Reddig Date: Mon, 27 Jan 2025 09:45:26 -0800 Subject: [PATCH 14/15] wit: fix switch --- wit/ident.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/wit/ident.go b/wit/ident.go index 13c894ae..958d01de 100644 --- a/wit/ident.go +++ b/wit/ident.go @@ -108,10 +108,10 @@ func validateName(s string) error { return errors.New("invalid character " + strconv.Quote(string(c))) } case c == '-': - switch { - case prev == 0: // start of string + switch prev { + case 0: // start of string return errors.New("invalid leading -") - case prev == '-': + case '-': return errors.New("invalid double --") } default: From bd158ac2cf9f567214aa7ad20a1e792e2f737855 Mon Sep 17 00:00:00 2001 From: Randy Reddig Date: Mon, 27 Jan 2025 10:09:21 -0800 Subject: [PATCH 15/15] wit: add additional name tests --- wit/ident_test.go | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/wit/ident_test.go b/wit/ident_test.go index c44355b9..4173eb2b 100644 --- a/wit/ident_test.go +++ b/wit/ident_test.go @@ -24,6 +24,10 @@ func TestIdent(t *testing.T) { {"%use:%own/%type", Ident{Namespace: "use", Package: "own", Extension: "type"}, false}, {"%use:%own/%type@0.2.0", Ident{Namespace: "use", Package: "own", Extension: "type", Version: semver.New("0.2.0")}, false}, + // Mixed-case + {"ABC:def-GHI", Ident{Namespace: "ABC", Package: "def-GHI"}, false}, + {"ABC1:def2-GHI3", Ident{Namespace: "ABC1", Package: "def2-GHI3"}, false}, + // Errors {"", Ident{}, true}, {":", Ident{}, true}, @@ -35,6 +39,14 @@ func TestIdent(t *testing.T) { {"wasi:clocks@", Ident{}, true}, {"wasi:clocks/wall-clock@", Ident{}, true}, {"foo%:bar%baz", Ident{Namespace: "foo%", Package: "bar%baz"}, true}, + {"-foo:bar", Ident{Namespace: "-foo", Package: "bar"}, true}, + {"foo-:bar", Ident{Namespace: "foo-", Package: "bar"}, true}, + {"foo--foo:bar", Ident{Namespace: "foo--foo", Package: "bar"}, true}, + {"aBc:bar", Ident{Namespace: "aBc", Package: "bar"}, true}, + {"1:2", Ident{Namespace: "1", Package: "2"}, true}, + {"1a:2b", Ident{Namespace: "1a", Package: "2b"}, true}, + {"foo-1:bar", Ident{Namespace: "foo-1", Package: "bar"}, true}, + {"foo:bar-1", Ident{Namespace: "foo", Package: "bar-2"}, true}, } for _, tt := range tests { t.Run(tt.s, func(t *testing.T) {