Skip to content

Commit 0be034b

Browse files
committed
internal/aliases: Adds an internal alias package.
Adds a transitional package for handling types.Alias until GoVersion>=1.26 for x/tools. Updates golang/go#65294 Change-Id: I7a58cb9ceb9945529baf14d33543dbebc23af542 Reviewed-on: https://go-review.googlesource.com/c/tools/+/559995 Reviewed-by: Robert Findley <[email protected]> TryBot-Result: Gopher Robot <[email protected]> LUCI-TryBot-Result: Go LUCI <[email protected]> Run-TryBot: Tim King <[email protected]>
1 parent 8efa10c commit 0be034b

File tree

4 files changed

+205
-0
lines changed

4 files changed

+205
-0
lines changed

internal/aliases/aliases.go

+28
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
// Copyright 2024 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+
package aliases
6+
7+
import (
8+
"go/token"
9+
"go/types"
10+
)
11+
12+
// Package aliases defines backward compatible shims
13+
// for the types.Alias type representation added in 1.22.
14+
// This defines placeholders for x/tools until 1.26.
15+
16+
// NewAlias creates a new TypeName in Package pkg that
17+
// is an alias for the type rhs.
18+
//
19+
// When GoVersion>=1.22 and GODEBUG=gotypesalias=1,
20+
// the Type() of the return value is a *types.Alias.
21+
func NewAlias(pos token.Pos, pkg *types.Package, name string, rhs types.Type) *types.TypeName {
22+
if enabled() {
23+
tname := types.NewTypeName(pos, pkg, name, nil)
24+
newAlias(tname, rhs)
25+
return tname
26+
}
27+
return types.NewTypeName(pos, pkg, name, rhs)
28+
}

internal/aliases/aliases_go121.go

+30
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
// Copyright 2024 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+
//go:build !go1.22
6+
// +build !go1.22
7+
8+
package aliases
9+
10+
import (
11+
"go/types"
12+
)
13+
14+
// Alias is a placeholder for a go/types.Alias for <=1.21.
15+
// It will never be created by go/types.
16+
type Alias struct{}
17+
18+
func (*Alias) String() string { panic("unreachable") }
19+
20+
func (*Alias) Underlying() types.Type { panic("unreachable") }
21+
22+
func (*Alias) Obj() *types.TypeName { panic("unreachable") }
23+
24+
// Unalias returns the type t for go <=1.21.
25+
func Unalias(t types.Type) types.Type { return t }
26+
27+
// Always false for go <=1.21. Ignores GODEBUG.
28+
func enabled() bool { return false }
29+
30+
func newAlias(name *types.TypeName, rhs types.Type) *Alias { panic("unreachable") }

internal/aliases/aliases_go122.go

+72
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
// Copyright 2024 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+
//go:build go1.22
6+
// +build go1.22
7+
8+
package aliases
9+
10+
import (
11+
"go/ast"
12+
"go/parser"
13+
"go/token"
14+
"go/types"
15+
"os"
16+
"strings"
17+
"sync"
18+
)
19+
20+
// Alias is an alias of types.Alias.
21+
type Alias = types.Alias
22+
23+
// Unalias is a wrapper of types.Unalias.
24+
func Unalias(t types.Type) types.Type { return types.Unalias(t) }
25+
26+
// newAlias is an internal alias around types.NewAlias.
27+
// Direct usage is discouraged as the moment.
28+
// Try to use NewAlias instead.
29+
func newAlias(tname *types.TypeName, rhs types.Type) *Alias {
30+
a := types.NewAlias(tname, rhs)
31+
// TODO(go.dev/issue/65455): Remove kludgy workaround to set a.actual as a side-effect.
32+
Unalias(a)
33+
return a
34+
}
35+
36+
// enabled returns true when types.Aliases are enabled.
37+
func enabled() bool {
38+
// Use the gotypesalias value in GODEBUG if set.
39+
godebug := os.Getenv("GODEBUG")
40+
value := -1 // last set value.
41+
for _, f := range strings.Split(godebug, ",") {
42+
switch f {
43+
case "gotypesalias=1":
44+
value = 1
45+
case "gotypesalias=0":
46+
value = 0
47+
}
48+
}
49+
switch value {
50+
case 0:
51+
return false
52+
case 1:
53+
return true
54+
default:
55+
return aliasesDefault()
56+
}
57+
}
58+
59+
// aliasesDefault reports if aliases are enabled by default.
60+
func aliasesDefault() bool {
61+
// Dynamically check if Aliases will be produced from go/types.
62+
aliasesDefaultOnce.Do(func() {
63+
fset := token.NewFileSet()
64+
f, _ := parser.ParseFile(fset, "a.go", "package p; type A = int", 0)
65+
pkg, _ := new(types.Config).Check("p", fset, []*ast.File{f}, nil)
66+
_, gotypesaliasDefault = pkg.Scope().Lookup("A").Type().(*types.Alias)
67+
})
68+
return gotypesaliasDefault
69+
}
70+
71+
var gotypesaliasDefault bool
72+
var aliasesDefaultOnce sync.Once

internal/aliases/aliases_test.go

+75
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
// Copyright 2024 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+
package aliases_test
6+
7+
import (
8+
"go/ast"
9+
"go/parser"
10+
"go/token"
11+
"go/types"
12+
"os"
13+
"testing"
14+
15+
"golang.org/x/tools/internal/aliases"
16+
"golang.org/x/tools/internal/testenv"
17+
)
18+
19+
// Assert that Obj exists on Alias.
20+
var _ func(*aliases.Alias) *types.TypeName = (*aliases.Alias).Obj
21+
22+
// TestNewAlias tests that alias.NewAlias creates an alias of a type
23+
// whose underlying and Unaliased type is *Named.
24+
// When gotypesalias=1 and GoVersion >= 1.22, the type will
25+
// be an *aliases.Alias.
26+
func TestNewAlias(t *testing.T) {
27+
const source = `
28+
package P
29+
30+
type Named int
31+
`
32+
fset := token.NewFileSet()
33+
f, err := parser.ParseFile(fset, "hello.go", source, 0)
34+
if err != nil {
35+
t.Fatal(err)
36+
}
37+
38+
var conf types.Config
39+
pkg, err := conf.Check("P", fset, []*ast.File{f}, nil)
40+
if err != nil {
41+
t.Fatal(err)
42+
}
43+
44+
expr := `*Named`
45+
tv, err := types.Eval(fset, pkg, 0, expr)
46+
if err != nil {
47+
t.Fatalf("Eval(%s) failed: %v", expr, err)
48+
}
49+
50+
for _, godebug := range []string{"", "gotypesalias=1"} {
51+
t.Run(godebug, func(t *testing.T) {
52+
saved := os.Getenv("GODEBUG")
53+
defer os.Setenv("GODEBUG", saved)
54+
os.Setenv("GODEBUG", godebug) // non parallel.
55+
56+
A := aliases.NewAlias(token.NoPos, pkg, "A", tv.Type)
57+
if got, want := A.Name(), "A"; got != want {
58+
t.Errorf("Expected A.Name()==%q. got %q", want, got)
59+
}
60+
61+
if got, want := A.Type().Underlying(), tv.Type; got != want {
62+
t.Errorf("Expected A.Type().Underlying()==%q. got %q", want, got)
63+
}
64+
if got, want := aliases.Unalias(A.Type()), tv.Type; got != want {
65+
t.Errorf("Expected Unalias(A)==%q. got %q", want, got)
66+
}
67+
68+
if testenv.Go1Point() >= 22 && godebug == "gotypesalias=1" {
69+
if _, ok := A.Type().(*aliases.Alias); !ok {
70+
t.Errorf("Expected A.Type() to be a types.Alias(). got %q", A.Type())
71+
}
72+
}
73+
})
74+
}
75+
}

0 commit comments

Comments
 (0)