Skip to content
19 changes: 17 additions & 2 deletions impl.go
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,11 @@ func stripPaths(in string) string {
out := make([]rune, 0, len(runes))
for len(runes) > 0 {
// Find extent of path-like segment
isQuotedPath := false
Comment thread
EliSauder marked this conversation as resolved.
Outdated
if runes[0] == '"' {
runes = runes[1:]
isQuotedPath = true
}
n := slices.IndexFunc(runes, isNonPathRune)
seg := runes
if n >= 0 {
Expand All @@ -98,13 +103,22 @@ func stripPaths(in string) string {
break
}
runes = runes[n:]
if isQuotedPath && len(runes) > 0 {
Comment thread
EliSauder marked this conversation as resolved.
Outdated
runes = runes[1:]
}

// Copy non-path runes verbatim
n = slices.IndexFunc(runes, isPathRune)
seg = runes
if n >= 0 {
seg = seg[:n]
}
if n > 0 && seg[0] == '"' {
Comment thread
EliSauder marked this conversation as resolved.
Outdated
seg = seg[1:]
}
if n > 1 && seg[len(seg)-1] == '"' {
seg = seg[:len(seg)-1]
}
out = append(out, seg...)
if n == -1 {
break
Expand All @@ -127,7 +141,8 @@ func lastIndex[S ~[]E, E comparable](s S, v E) int {
// isPathRune reports whether r can appear in an import path.
// See https://go.dev/ref/spec#Import_declarations.
func isPathRune(r rune) bool {
return unicode.IsPrint(r) && !strings.ContainsRune(" \uFFFD!\"#$%&'()*,:;<=>?[\\]^`{|}", r)
Comment thread
EliSauder marked this conversation as resolved.
return unicode.IsPrint(r) &&
!strings.ContainsRune(" \uFFFD!\"#$%&'()*,:;<=>?[\\]^`{|}", r)
}

func isNonPathRune(r rune) bool {
Expand Down Expand Up @@ -175,7 +190,7 @@ func findInterface(input string, srcDir string) (path string, iface Type, err er
if dot <= slash {
return "", Type{}, fmt.Errorf("invalid interface name: %s", input)
}
path = baseInput[:dot]
path = strings.Trim(baseInput[:dot], "\"")
id := stripPaths(input[dot+1:])
iface, err = parseType(id)
if err != nil {
Expand Down
16 changes: 11 additions & 5 deletions impl_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ func TestFindInterface(t *testing.T) {
{input: "a/b/c/pkg", wantErr: true},
{input: "a/b/c/pkg.", wantErr: true},
{input: "a/b/c/pkg.Typ", path: "a/b/c/pkg", typ: Type{Name: "Typ"}},
{input: "\"a/b/c/pkg\".Typ", path: "a/b/c/pkg", typ: Type{Name: "Typ"}},
{input: "gopkg.in/yaml.v2.Unmarshaler", path: "gopkg.in/yaml.v2", typ: Type{Name: "Unmarshaler"}},
{input: "github.com/josharian/impl/testdata.GenericInterface1[string]", path: "github.com/josharian/impl/testdata", typ: Type{Name: "GenericInterface1", Params: []string{"string"}}},
{input: "github.com/josharian/impl/testdata.GenericInterface1[*string]", path: "github.com/josharian/impl/testdata", typ: Type{Name: "GenericInterface1", Params: []string{"*string"}}},
Expand Down Expand Up @@ -693,35 +694,35 @@ func TestStubGenerationForImplemented(t *testing.T) {
want string
}{
{
desc: "without implemeted methods",
desc: "without implemented methods",
iface: "github.com/josharian/impl/testdata.Interface3",
recv: "r *Implemented",
recvPkg: "testdata",
want: testdata.Interface4Output,
},
{
desc: "without implemeted methods with trailing space",
desc: "without implemented methods with trailing space",
iface: "github.com/josharian/impl/testdata.Interface3",
recv: "r *Implemented ",
recvPkg: "testdata",
want: testdata.Interface4Output,
},
{
desc: "without implemeted methods, with generic receiver",
desc: "without implemented methods, with generic receiver",
iface: "github.com/josharian/impl/testdata.Interface3",
recv: "r *ImplementedGeneric[Type1]",
recvPkg: "testdata",
want: testdata.Interface4GenericOutput,
},
{
desc: "without implemeted methods, with generic receiver with multiple params",
desc: "without implemented methods, with generic receiver with multiple params",
iface: "github.com/josharian/impl/testdata.Interface3",
recv: "r *ImplementedGenericMultipleParams[Type1, Type2]",
recvPkg: "testdata",
want: testdata.Interface4GenericMultipleParamsOutput,
},
{
desc: "without implemeted methods and receiver variable",
desc: "without implemented methods and receiver variable",
iface: "github.com/josharian/impl/testdata.Interface3",
recv: "*Implemented",
recvPkg: "testdata",
Expand Down Expand Up @@ -914,8 +915,13 @@ func TestStripPaths(t *testing.T) {
}{
{desc: "no path", input: "Iface", want: "Iface"},
{desc: "simple path", input: "a/b.T", want: "b.T"},
{desc: "simple quoted path", input: "\"a/b\".T", want: "b.T"},
Comment thread
EliSauder marked this conversation as resolved.
Outdated
{desc: "deep path", input: "github.com/foo/bar.T", want: "bar.T"},
{desc: "deep quoted path", input: "\"github.com/foo/bar\".T", want: "bar.T"},
{desc: "generic with path param", input: "Iface[github.com/foo/bar.T]", want: "Iface[bar.T]"},
{desc: "generic and interface with path param", input: "github.com/foo.Iface[github.com/foo/bar.T]", want: "foo.Iface[bar.T]"},
{desc: "generic and interface with quoted path param", input: "\"github.com/foo\".Iface[\"github.com/foo/bar\".T]", want: "foo.Iface[bar.T]"},
{desc: "generic with quoted path param", input: "Iface[\"github.com/foo/bar\".T]", want: "Iface[bar.T]"},
{desc: "multiple path params", input: "Iface[a/b.T, c/d.U]", want: "Iface[b.T, d.U]"},
{desc: "nested generic with paths", input: "Iface[a/b.Other[c/d.T]]", want: "Iface[b.Other[d.T]]"},
{desc: "pointer to path type", input: "Iface[*a/b.T]", want: "Iface[*b.T]"},
Expand Down