-
Notifications
You must be signed in to change notification settings - Fork 2.3k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
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 <rfindley@google.com> TryBot-Result: Gopher Robot <gobot@golang.org> LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com> Run-TryBot: Tim King <taking@google.com>
- Loading branch information
1 parent
8efa10c
commit 0be034b
Showing
4 changed files
with
205 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
// Copyright 2024 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 aliases | ||
|
||
import ( | ||
"go/token" | ||
"go/types" | ||
) | ||
|
||
// Package aliases defines backward compatible shims | ||
// for the types.Alias type representation added in 1.22. | ||
// This defines placeholders for x/tools until 1.26. | ||
|
||
// NewAlias creates a new TypeName in Package pkg that | ||
// is an alias for the type rhs. | ||
// | ||
// When GoVersion>=1.22 and GODEBUG=gotypesalias=1, | ||
// the Type() of the return value is a *types.Alias. | ||
func NewAlias(pos token.Pos, pkg *types.Package, name string, rhs types.Type) *types.TypeName { | ||
if enabled() { | ||
tname := types.NewTypeName(pos, pkg, name, nil) | ||
newAlias(tname, rhs) | ||
return tname | ||
} | ||
return types.NewTypeName(pos, pkg, name, rhs) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
// Copyright 2024 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. | ||
|
||
//go:build !go1.22 | ||
// +build !go1.22 | ||
|
||
package aliases | ||
|
||
import ( | ||
"go/types" | ||
) | ||
|
||
// Alias is a placeholder for a go/types.Alias for <=1.21. | ||
// It will never be created by go/types. | ||
type Alias struct{} | ||
|
||
func (*Alias) String() string { panic("unreachable") } | ||
|
||
func (*Alias) Underlying() types.Type { panic("unreachable") } | ||
|
||
func (*Alias) Obj() *types.TypeName { panic("unreachable") } | ||
|
||
// Unalias returns the type t for go <=1.21. | ||
func Unalias(t types.Type) types.Type { return t } | ||
|
||
// Always false for go <=1.21. Ignores GODEBUG. | ||
func enabled() bool { return false } | ||
|
||
func newAlias(name *types.TypeName, rhs types.Type) *Alias { panic("unreachable") } |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,72 @@ | ||
// Copyright 2024 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. | ||
|
||
//go:build go1.22 | ||
// +build go1.22 | ||
|
||
package aliases | ||
|
||
import ( | ||
"go/ast" | ||
"go/parser" | ||
"go/token" | ||
"go/types" | ||
"os" | ||
"strings" | ||
"sync" | ||
) | ||
|
||
// Alias is an alias of types.Alias. | ||
type Alias = types.Alias | ||
|
||
// Unalias is a wrapper of types.Unalias. | ||
func Unalias(t types.Type) types.Type { return types.Unalias(t) } | ||
|
||
// newAlias is an internal alias around types.NewAlias. | ||
// Direct usage is discouraged as the moment. | ||
// Try to use NewAlias instead. | ||
func newAlias(tname *types.TypeName, rhs types.Type) *Alias { | ||
a := types.NewAlias(tname, rhs) | ||
// TODO(go.dev/issue/65455): Remove kludgy workaround to set a.actual as a side-effect. | ||
Unalias(a) | ||
return a | ||
} | ||
|
||
// enabled returns true when types.Aliases are enabled. | ||
func enabled() bool { | ||
// Use the gotypesalias value in GODEBUG if set. | ||
godebug := os.Getenv("GODEBUG") | ||
value := -1 // last set value. | ||
for _, f := range strings.Split(godebug, ",") { | ||
switch f { | ||
case "gotypesalias=1": | ||
value = 1 | ||
case "gotypesalias=0": | ||
value = 0 | ||
} | ||
} | ||
switch value { | ||
case 0: | ||
return false | ||
case 1: | ||
return true | ||
default: | ||
return aliasesDefault() | ||
} | ||
} | ||
|
||
// aliasesDefault reports if aliases are enabled by default. | ||
func aliasesDefault() bool { | ||
// Dynamically check if Aliases will be produced from go/types. | ||
aliasesDefaultOnce.Do(func() { | ||
fset := token.NewFileSet() | ||
f, _ := parser.ParseFile(fset, "a.go", "package p; type A = int", 0) | ||
pkg, _ := new(types.Config).Check("p", fset, []*ast.File{f}, nil) | ||
_, gotypesaliasDefault = pkg.Scope().Lookup("A").Type().(*types.Alias) | ||
}) | ||
return gotypesaliasDefault | ||
} | ||
|
||
var gotypesaliasDefault bool | ||
var aliasesDefaultOnce sync.Once |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,75 @@ | ||
// Copyright 2024 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 aliases_test | ||
|
||
import ( | ||
"go/ast" | ||
"go/parser" | ||
"go/token" | ||
"go/types" | ||
"os" | ||
"testing" | ||
|
||
"golang.org/x/tools/internal/aliases" | ||
"golang.org/x/tools/internal/testenv" | ||
) | ||
|
||
// Assert that Obj exists on Alias. | ||
var _ func(*aliases.Alias) *types.TypeName = (*aliases.Alias).Obj | ||
|
||
// TestNewAlias tests that alias.NewAlias creates an alias of a type | ||
// whose underlying and Unaliased type is *Named. | ||
// When gotypesalias=1 and GoVersion >= 1.22, the type will | ||
// be an *aliases.Alias. | ||
func TestNewAlias(t *testing.T) { | ||
const source = ` | ||
package P | ||
type Named int | ||
` | ||
fset := token.NewFileSet() | ||
f, err := parser.ParseFile(fset, "hello.go", source, 0) | ||
if err != nil { | ||
t.Fatal(err) | ||
} | ||
|
||
var conf types.Config | ||
pkg, err := conf.Check("P", fset, []*ast.File{f}, nil) | ||
if err != nil { | ||
t.Fatal(err) | ||
} | ||
|
||
expr := `*Named` | ||
tv, err := types.Eval(fset, pkg, 0, expr) | ||
if err != nil { | ||
t.Fatalf("Eval(%s) failed: %v", expr, err) | ||
} | ||
|
||
for _, godebug := range []string{"", "gotypesalias=1"} { | ||
t.Run(godebug, func(t *testing.T) { | ||
saved := os.Getenv("GODEBUG") | ||
defer os.Setenv("GODEBUG", saved) | ||
os.Setenv("GODEBUG", godebug) // non parallel. | ||
|
||
A := aliases.NewAlias(token.NoPos, pkg, "A", tv.Type) | ||
if got, want := A.Name(), "A"; got != want { | ||
t.Errorf("Expected A.Name()==%q. got %q", want, got) | ||
} | ||
|
||
if got, want := A.Type().Underlying(), tv.Type; got != want { | ||
t.Errorf("Expected A.Type().Underlying()==%q. got %q", want, got) | ||
} | ||
if got, want := aliases.Unalias(A.Type()), tv.Type; got != want { | ||
t.Errorf("Expected Unalias(A)==%q. got %q", want, got) | ||
} | ||
|
||
if testenv.Go1Point() >= 22 && godebug == "gotypesalias=1" { | ||
if _, ok := A.Type().(*aliases.Alias); !ok { | ||
t.Errorf("Expected A.Type() to be a types.Alias(). got %q", A.Type()) | ||
} | ||
} | ||
}) | ||
} | ||
} |