Skip to content

Commit

Permalink
require go 1.18
Browse files Browse the repository at this point in the history
  • Loading branch information
nishanths committed Jun 9, 2023
1 parent f1d6788 commit 5e49854
Show file tree
Hide file tree
Showing 13 changed files with 167 additions and 257 deletions.
8 changes: 4 additions & 4 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ jobs:
all:
strategy:
matrix:
go-version: [1.14, 1.16, 1.19, 1.x]
go-version: [1.18, 1.19, 1.x]
os: [ubuntu-latest, macos-latest, windows-latest]
runs-on: ${{ matrix.os }}

Expand All @@ -20,7 +20,7 @@ jobs:
- name: build
run: make build

# for earlier go versions, staticcheck build fails due to:
# For earlier go versions, staticcheck build fails due to:
#
# ../../../go/pkg/mod/honnef.co/go/tools@v0.3.3/go/ir/builder.go:36:2:
# //go:build comment without // +build comment
Expand All @@ -32,11 +32,11 @@ jobs:
# or method Origin)
# note: module requires Go 1.19
#
- name: install vet tools (go1.19 or later)
- name: install vet tools (go 1.19 or later)
if: ${{ matrix.go-version >= '1.19' }}
run: make install-vet

- name: vet (go1.19 or later)
- name: vet (go 1.19 or later)
if: ${{ matrix.go-version >= '1.19' }}
run: make vet

Expand Down
192 changes: 150 additions & 42 deletions common.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,127 @@ import (
"sort"
"strings"

"golang.org/x/tools/go/analysis"
"golang.org/x/tools/go/ast/astutil"
)

// enumTypeAndMembers combines an enumType and its members set.
type enumTypeAndMembers struct {
typ enumType
members enumMembers
}

func fromNamed(pass *analysis.Pass, t *types.Named, typeparam bool) (result []enumTypeAndMembers, ok bool) {
if tpkg := t.Obj().Pkg(); tpkg == nil {
// go/types documentation says: nil for labels and
// objects in the Universe scope. This happens for the built-in
// error type for example.
return nil, false // not a valid enum type, so ok == false
}

et := enumType{t.Obj()}
if em, ok := importFact(pass, et); ok {
return []enumTypeAndMembers{{et, em}}, true
}

if typeparam {
// is it a named interface?
if intf, ok := t.Underlying().(*types.Interface); ok {
return fromInterface(pass, intf, typeparam)
}
}

return nil, false // not a valid enum type, so ok == false
}

func fromInterface(pass *analysis.Pass, intf *types.Interface, typeparam bool) (result []enumTypeAndMembers, ok bool) {
allOk := true
for i := 0; i < intf.NumEmbeddeds(); i++ {
r, ok := fromType(pass, intf.EmbeddedType(i), typeparam)
result = append(result, r...)
allOk = allOk && ok
}
return result, allOk
}

func fromUnion(pass *analysis.Pass, union *types.Union, typeparam bool) (result []enumTypeAndMembers, ok bool) {
allOk := true
// gather from each term in the union.
for i := 0; i < union.Len(); i++ {
r, ok := fromType(pass, union.Term(i).Type(), typeparam)
result = append(result, r...)
allOk = allOk && ok
}
return result, allOk
}

func fromTypeParam(pass *analysis.Pass, tp *types.TypeParam, typeparam bool) (result []enumTypeAndMembers, ok bool) {
// Does not appear to be explicitly documented, but based on Go language
// spec (see section Type constraints) and Go standard library source code,
// we can expect constraints to have underlying type *types.Interface
// Regardless it will be handled in fromType.
return fromType(pass, tp.Constraint().Underlying(), typeparam)
}

func fromType(pass *analysis.Pass, t types.Type, typeparam bool) (result []enumTypeAndMembers, ok bool) {
switch t := t.(type) {
case *types.Named:
return fromNamed(pass, t, typeparam)

case *types.Union:
return fromUnion(pass, t, typeparam)

case *types.TypeParam:
return fromTypeParam(pass, t, typeparam)

case *types.Interface:
if !typeparam {
return nil, true
}
// anonymous interface.
// e.g. func foo[T interface { M } | interface { N }](v T) {}
return fromInterface(pass, t, typeparam)

default:
// ignore these.
return nil, true
}
}

func composingEnumTypes(pass *analysis.Pass, t types.Type) (result []enumTypeAndMembers, ok bool) {
_, typeparam := t.(*types.TypeParam)
result, ok = fromType(pass, t, typeparam)

if typeparam {
var kind types.BasicKind
var kindSet bool

// sameBasicKind reports whether each type t that the function is called
// with has the same underlying basic kind.
sameBasicKind := func(t types.Type) (ok bool) {
basic, ok := t.Underlying().(*types.Basic)
if !ok {
return false
}
if kindSet && kind != basic.Kind() {
return false
}
kind = basic.Kind()
kindSet = true
return true
}

for _, rr := range result {
if !sameBasicKind(rr.typ.TypeName.Type()) {
ok = false
break
}
}
}

return result, ok
}

func denotesPackage(ident *ast.Ident, info *types.Info) bool {
obj := info.ObjectOf(ident)
if obj == nil {
Expand All @@ -37,19 +155,18 @@ func exprConstVal(e ast.Expr, info *types.Info) (constantValue, bool) {
// There are two scenarios.
// See related test cases in typealias/quux/quux.go.
//
// Scenario 1
// # Scenario 1
//
// Tag package and constant package are the same. This is
// simple; we just use fs.ModeDir's value.
//
// Example:
//
// var mode fs.FileMode
// switch mode {
// case fs.ModeDir:
// }
// var mode fs.FileMode
// switch mode {
// case fs.ModeDir:
// }
//
// Scenario 2
// # Scenario 2
//
// Tag package and constant package are different. In this
// scenario, too, we accept the case clause expr constant value,
Expand All @@ -58,19 +175,19 @@ func exprConstVal(e ast.Expr, info *types.Info) (constantValue, bool) {
//
// Example:
//
// var mode fs.FileMode
// switch mode {
// case os.ModeDir:
// }
// var mode fs.FileMode
// switch mode {
// case os.ModeDir:
// }
//
// Or equivalently:
//
// // The type of mode is effectively fs.FileMode,
// // due to type alias.
// var mode os.FileMode
// switch mode {
// case os.ModeDir:
// }
// // The type of mode is effectively fs.FileMode,
// // due to type alias.
// var mode os.FileMode
// switch mode {
// case os.ModeDir:
// }
return determineConstVal(ident, info), true
}

Expand Down Expand Up @@ -136,12 +253,6 @@ type member struct {
val constantValue
}

// typeAndMembers combines an enumType and its members set.
type typeAndMembers struct {
et enumType
em enumMembers
}

type checklist struct {
info map[enumType]enumMembers
checkl map[member]struct{}
Expand Down Expand Up @@ -310,10 +421,10 @@ func diagnosticGroups(gs []group) string {
return strings.Join(out, ", ")
}

func toEnumTypes(es []typeAndMembers) []enumType {
func toEnumTypes(es []enumTypeAndMembers) []enumType {
out := make([]enumType, len(es))
for i := range es {
out[i] = es[i].et
out[i] = es[i].typ
}
return out
}
Expand All @@ -332,35 +443,32 @@ func dedupEnumTypes(types []enumType) []enumType {
return ret
}

// If dropping pre-go1.18 support, the following types and functions are
// candidates for generics.

type boolCache struct {
m map[*ast.File]bool
value func(*ast.File) bool
m map[*ast.File]bool
compute func(*ast.File) bool
}

func (c boolCache) get(file *ast.File) bool {
if c.m == nil {
c.m = make(map[*ast.File]bool)
}
func (c *boolCache) get(file *ast.File) bool {
if _, ok := c.m[file]; !ok {
c.m[file] = c.value(file)
if c.m == nil {
c.m = make(map[*ast.File]bool)
}
c.m[file] = c.compute(file)
}
return c.m[file]
}

type commentCache struct {
m map[*ast.File]ast.CommentMap
value func(*token.FileSet, *ast.File) ast.CommentMap
m map[*ast.File]ast.CommentMap
compute func(*token.FileSet, *ast.File) ast.CommentMap
}

func (c commentCache) get(fset *token.FileSet, file *ast.File) ast.CommentMap {
if c.m == nil {
c.m = make(map[*ast.File]ast.CommentMap)
}
func (c *commentCache) get(fset *token.FileSet, file *ast.File) ast.CommentMap {
if _, ok := c.m[file]; !ok {
c.m[file] = c.value(fset, file)
if c.m == nil {
c.m = make(map[*ast.File]ast.CommentMap)
}
c.m[file] = c.compute(fset, file)
}
return c.m[file]
}
Loading

0 comments on commit 5e49854

Please sign in to comment.