// 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) } }) } }