Skip to content

Commit

Permalink
internal/aliases: Adds an internal alias package.
Browse files Browse the repository at this point in the history
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
timothy-king committed Feb 5, 2024
1 parent 8efa10c commit 0be034b
Show file tree
Hide file tree
Showing 4 changed files with 205 additions and 0 deletions.
28 changes: 28 additions & 0 deletions internal/aliases/aliases.go
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)
}
30 changes: 30 additions & 0 deletions internal/aliases/aliases_go121.go
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") }
72 changes: 72 additions & 0 deletions internal/aliases/aliases_go122.go
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
75 changes: 75 additions & 0 deletions internal/aliases/aliases_test.go
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())
}
}
})
}
}

0 comments on commit 0be034b

Please sign in to comment.