blob: ffabe584cb820b4472e53b35922aeaf0f5080db2 [file] [log] [blame] [edit]
// Copyright 2025 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package ast
import (
"go/token"
"reflect"
"strings"
"testing"
)
func TestParseDirectiveMatchesIsDirective(t *testing.T) {
for _, tt := range isDirectiveTests {
want := tt.ok
if strings.HasPrefix(tt.in, "extern ") || strings.HasPrefix(tt.in, "export ") {
// ParseDirective does NOT support extern or export, unlike
// isDirective.
want = false
}
if _, ok := ParseDirective(0, "//"+tt.in); ok != want {
t.Errorf("ParseDirective(0, %q) = %v, want %v", "// "+tt.in, ok, want)
}
}
}
func TestParseDirective(t *testing.T) {
for _, test := range []struct {
name string
in string
pos token.Pos
want Directive
wantOK bool
}{
{
name: "valid",
in: "//go:generate stringer -type Op -trimprefix Op",
pos: 10,
want: Directive{
Tool: "go",
Name: "generate",
Args: "stringer -type Op -trimprefix Op",
Slash: 10,
ArgsPos: token.Pos(10 + len("//go:generate ")),
},
wantOK: true,
},
{
name: "no args",
in: "//go:build ignore",
pos: 20,
want: Directive{
Tool: "go",
Name: "build",
Args: "ignore",
Slash: 20,
ArgsPos: token.Pos(20 + len("//go:build ")),
},
wantOK: true,
},
{
name: "not a directive",
in: "// not a directive",
pos: 30,
wantOK: false,
},
{
name: "not a comment",
in: "go:generate",
pos: 40,
wantOK: false,
},
{
name: "empty",
in: "",
pos: 50,
wantOK: false,
},
{
name: "just slashes",
in: "//",
pos: 60,
wantOK: false,
},
{
name: "no name",
in: "//go:",
pos: 70,
wantOK: false,
},
{
name: "no tool",
in: "//:generate",
pos: 80,
wantOK: false,
},
{
name: "multiple spaces",
in: "//go:build foo bar",
pos: 90,
want: Directive{
Tool: "go",
Name: "build",
Args: "foo bar",
Slash: 90,
ArgsPos: token.Pos(90 + len("//go:build ")),
},
wantOK: true,
},
{
name: "trailing space",
in: "//go:build foo ",
pos: 100,
want: Directive{
Tool: "go",
Name: "build",
Args: "foo",
Slash: 100,
ArgsPos: token.Pos(100 + len("//go:build ")),
},
wantOK: true,
},
} {
t.Run(test.name, func(t *testing.T) {
got, gotOK := ParseDirective(test.pos, test.in)
if gotOK != test.wantOK {
t.Fatalf("ParseDirective(%q) ok = %v, want %v", test.in, gotOK, test.wantOK)
}
if !reflect.DeepEqual(got, test.want) {
t.Errorf("ParseDirective(%q) = %+v, want %+v", test.in, got, test.want)
}
})
}
}
func TestParseArgs(t *testing.T) {
for _, test := range []struct {
name string
in Directive
want []DirectiveArg
wantErr bool
}{
{
name: "simple",
in: Directive{
Tool: "go",
Name: "generate",
Args: "stringer -type Op",
ArgsPos: 10,
},
want: []DirectiveArg{
{"stringer", 10},
{"-type", token.Pos(10 + len("stringer "))},
{"Op", token.Pos(10 + len("stringer -type "))},
},
},
{
name: "quoted",
in: Directive{
Tool: "go",
Name: "generate",
Args: "\"foo bar\" baz",
ArgsPos: 10,
},
want: []DirectiveArg{
{"foo bar", 10},
{"baz", token.Pos(10 + len("\"foo bar\" "))},
},
},
{
name: "raw quoted",
in: Directive{
Tool: "go",
Name: "generate",
Args: "`foo bar` baz",
ArgsPos: 10,
},
want: []DirectiveArg{
{"foo bar", 10},
{"baz", token.Pos(10 + len("`foo bar` "))},
},
},
{
name: "escapes",
in: Directive{
Tool: "go",
Name: "generate",
Args: "\"foo\\U0001F60Abar\" `a\\tb`",
ArgsPos: 10,
},
want: []DirectiveArg{
{"foo😊bar", 10},
{"a\\tb", token.Pos(10 + len("\"foo\\U0001F60Abar\" "))},
},
},
{
name: "empty args",
in: Directive{
Tool: "go",
Name: "build",
Args: "",
ArgsPos: 10,
},
want: []DirectiveArg{},
},
{
name: "spaces",
in: Directive{
Tool: "go",
Name: "build",
Args: " foo bar ",
ArgsPos: 10,
},
want: []DirectiveArg{
{"foo", token.Pos(10 + len(" "))},
{"bar", token.Pos(10 + len(" foo "))},
},
},
{
name: "unterminated quote",
in: Directive{
Tool: "go",
Name: "generate",
Args: "`foo",
},
wantErr: true,
},
{
name: "no space after quote",
in: Directive{
Tool: "go",
Name: "generate",
Args: `"foo"bar`,
},
wantErr: true,
},
} {
t.Run(test.name, func(t *testing.T) {
got, err := test.in.ParseArgs()
if err != nil && !test.wantErr {
t.Errorf("got ParseArgs(%+v) = error %s; want %+v", test.in, err, test.want)
} else if err == nil && test.wantErr {
t.Errorf("got ParseArgs(%+v) = %+v; want error", test.in, got)
} else if err == nil && !reflect.DeepEqual(got, test.want) {
t.Errorf("got ParseArgs(%+v) = %+v; want %+v", test.in, got, test.want)
}
})
}
}