Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

typesutil.Check #25

Merged
merged 1 commit into from
Oct 20, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions gopls/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@ go 1.18

require (
github.com/google/go-cmp v0.5.9
github.com/goplus/gop v1.1.4-0.20231018115918-ae7cf5030c9b
github.com/goplus/gop v1.1.4-0.20231020103555-4992fd2a2f7e
github.com/goplus/mod v0.11.8-0.20231019172744-da5848421263
github.com/jba/printsrc v0.2.2
github.com/jba/templatecheck v0.6.0
github.com/sergi/go-diff v1.1.0
Expand All @@ -23,8 +24,7 @@ require (
require (
github.com/BurntSushi/toml v1.2.1 // indirect
github.com/google/safehtml v0.1.0 // indirect
github.com/goplus/gox v1.12.5 // indirect
github.com/goplus/mod v0.11.7 // indirect
github.com/goplus/gox v1.12.2-0.20231020050546-7a7af254a518 // indirect
github.com/qiniu/x v1.13.1 // indirect
golang.org/x/exp v0.0.0-20220722155223-a9213eeb770e // indirect
golang.org/x/exp/typeparams v0.0.0-20221212164502-fae10dda9338 // indirect
Expand Down
12 changes: 6 additions & 6 deletions gopls/go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,12 @@ github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeN
github.com/google/safehtml v0.0.2/go.mod h1:L4KWwDsUJdECRAEpZoBn3O64bQaywRscowZjJAzjHnU=
github.com/google/safehtml v0.1.0 h1:EwLKo8qawTKfsi0orxcQAZzu07cICaBeFMegAU9eaT8=
github.com/google/safehtml v0.1.0/go.mod h1:L4KWwDsUJdECRAEpZoBn3O64bQaywRscowZjJAzjHnU=
github.com/goplus/gop v1.1.4-0.20231018115918-ae7cf5030c9b h1:+p2DtBnOyLuLz0r7gFguSpEcAq7HZEbiNPu7NyTkCPQ=
github.com/goplus/gop v1.1.4-0.20231018115918-ae7cf5030c9b/go.mod h1:EXUQdMiWcT8lWbtSt9UqE74vboZg1yAU46u+nuDCXNA=
github.com/goplus/gox v1.12.5 h1:YkdsZDIOW3DZLJctWYKVzdsqMeuCYNn7J4BsFi6B058=
github.com/goplus/gox v1.12.5/go.mod h1:ferIrvJSoXae8gABk5Z4olEzfAxZpUy4hD91Vm5E/qs=
github.com/goplus/mod v0.11.7 h1:OTGFi/Jk0qmTy3ih8u3BQ6CbtcaPlTrJBlTacnge0jE=
github.com/goplus/mod v0.11.7/go.mod h1:yl2QncBKTdXk+8UaNsdo4u2zSpGEJYA5JKjgD3K2h00=
github.com/goplus/gop v1.1.4-0.20231020103555-4992fd2a2f7e h1:HZ4EJNCeywOzzXUlp1IPBMtbeJopSuUNVPvVrOc6VzQ=
github.com/goplus/gop v1.1.4-0.20231020103555-4992fd2a2f7e/go.mod h1:SLnQ0501eW85N/w+sBLyrF2lhnKFKxCneiq+0IBV2eU=
github.com/goplus/gox v1.12.2-0.20231020050546-7a7af254a518 h1:s9CsA13IvxuCkOLrJkPmbS+Tilof5i2zIdQtyL5+USw=
github.com/goplus/gox v1.12.2-0.20231020050546-7a7af254a518/go.mod h1:Ek1YIy3wRaZ1i0DD2XG29i3r5AFdhcOradK0/GGs1YQ=
github.com/goplus/mod v0.11.8-0.20231019172744-da5848421263 h1:PE0HveOss5mai9pa52L4/ZvVqZtltogJ9rIUIsdlG/I=
github.com/goplus/mod v0.11.8-0.20231019172744-da5848421263/go.mod h1:yl2QncBKTdXk+8UaNsdo4u2zSpGEJYA5JKjgD3K2h00=
github.com/jba/printsrc v0.2.2 h1:9OHK51UT+/iMAEBlQIIXW04qvKyF3/vvLuwW/hL8tDU=
github.com/jba/printsrc v0.2.2/go.mod h1:1xULjw59sL0dPdWpDoVU06TIEO/Wnfv6AHRpiElTwYM=
github.com/jba/templatecheck v0.6.0 h1:SwM8C4hlK/YNLsdcXStfnHWE2HKkuTVwy5FKQHt5ro8=
Expand Down
26 changes: 23 additions & 3 deletions gopls/internal/goxls/packages/packages.go
Original file line number Diff line number Diff line change
Expand Up @@ -166,18 +166,32 @@ func pkgOf(pkgMap map[*packages.Package]*Package, pkg *packages.Package) *Packag
return ret
}
ret := &Package{Package: *pkg, Imports: importPkgs(pkgMap, pkg.Imports)}
for _, file := range pkg.CompiledGoFiles {
for i, file := range pkg.CompiledGoFiles {
dir, fname := filepath.Split(file)
if strings.HasPrefix(fname, "gop_autogen") { // has Go+ files
addGopFiles(ret, dir)
addGopFiles(ret, dir, isGoTestFile(fname) || hasGoTestFile(pkg.CompiledGoFiles[i+1:]))
break
}
}
pkgMap[pkg] = ret
return ret
}

func addGopFiles(ret *Package, dir string) {
func hasGoTestFile(goFiles []string) bool {
for _, file := range goFiles {
fname := filepath.Base(file)
if isGoTestFile(fname) {
return true
}
}
return false
}

func isGoTestFile(fname string) bool {
return strings.HasSuffix(fname, "_test.go")
}

func addGopFiles(ret *Package, dir string, test bool) {
entries, err := os.ReadDir(dir)
if err != nil {
return
Expand All @@ -186,10 +200,16 @@ func addGopFiles(ret *Package, dir string) {
pkgName := ret.Name
for _, e := range entries {
fname := e.Name()
if strings.HasPrefix(fname, "_") {
continue
}
fext := path.Ext(fname)
if goputil.FileKind(fext) == 0 {
continue
}
if !test && strings.HasSuffix(fname[:len(fname)-len(fext)], "_test") {
continue
}
file := dir + fname
f, err := parser.ParseFile(fset, file, nil, parser.PackageClauseOnly)
if err == nil && pkgName == f.Name.Name {
Expand Down
43 changes: 26 additions & 17 deletions gopls/internal/goxls/typesutil/check.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,33 +5,42 @@
package typesutil

import (
goast "go/ast"
"go/types"

"github.com/goplus/gop/ast"
"github.com/goplus/gop/token"
"github.com/goplus/gop/x/c2go"
"github.com/goplus/gop/x/typesutil"
"github.com/goplus/mod/gopmod"
)

// A Checker maintains the state of the type checker.
// It must be created with NewChecker.
type Checker struct {
c *types.Checker
type Checker = typesutil.Checker

type Config struct {
// Types provides type information for the package (optional).
Types *types.Package

// Fset provides source position information for syntax trees and types (required).
// If Fset is nil, Load will use a new fileset, but preserve Fset's value.
Fset *token.FileSet

// Mod represents a gop.mod object.
Mod *gopmod.Module
}

// NewChecker returns a new Checker instance for a given package.
// Package files may be added incrementally via checker.Files.
func NewChecker(
conf *types.Config, fset *token.FileSet, pkg *types.Package,
goInfo *types.Info, gopInfo *Info) *Checker {
check := types.NewChecker(conf, fset, pkg, goInfo)
return &Checker{check}
}

// Files checks the provided files as part of the checker's package.
func (p *Checker) Files(goFiles []*goast.File, gopFiles []*ast.File) error {
if len(gopFiles) == 0 {
return p.c.Files(goFiles)
func NewChecker(conf *types.Config, opts *Config, goInfo *types.Info, gopInfo *Info) *Checker {
mod := opts.Mod
if mod == nil {
mod = gopmod.Default
}
chkOpts := &typesutil.Config{
Types: opts.Types,
Fset: opts.Fset,
LookupPub: c2go.LookupPub(mod),
LookupClass: mod.LookupClass,
}
// goxls: todo
return nil
return typesutil.NewChecker(conf, chkOpts, goInfo, gopInfo)
}
141 changes: 2 additions & 139 deletions gopls/internal/goxls/typesutil/typesutil.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,148 +5,11 @@
package typesutil

import (
"go/types"

"github.com/goplus/gop/ast"
"github.com/goplus/gop/x/typesutil"
)

// An Initializer describes a package-level variable, or a list of variables in case
// of a multi-valued initialization expression, and the corresponding initialization
// expression.
type Initializer struct {
Lhs []*types.Var // var Lhs = Rhs
Rhs ast.Expr
}

// Info holds result type information for a type-checked package.
// Only the information for which a map is provided is collected.
// If the package has type errors, the collected information may
// be incomplete.
type Info struct {
// Types maps expressions to their types, and for constant
// expressions, also their values. Invalid expressions are
// omitted.
//
// For (possibly parenthesized) identifiers denoting built-in
// functions, the recorded signatures are call-site specific:
// if the call result is not a constant, the recorded type is
// an argument-specific signature. Otherwise, the recorded type
// is invalid.
//
// The Types map does not record the type of every identifier,
// only those that appear where an arbitrary expression is
// permitted. For instance, the identifier f in a selector
// expression x.f is found only in the Selections map, the
// identifier z in a variable declaration 'var z int' is found
// only in the Defs map, and identifiers denoting packages in
// qualified identifiers are collected in the Uses map.
Types map[ast.Expr]types.TypeAndValue

// Instances maps identifiers denoting generic types or functions to their
// type arguments and instantiated type.
//
// For example, Instances will map the identifier for 'T' in the type
// instantiation T[int, string] to the type arguments [int, string] and
// resulting instantiated *Named type. Given a generic function
// func F[A any](A), Instances will map the identifier for 'F' in the call
// expression F(int(1)) to the inferred type arguments [int], and resulting
// instantiated *Signature.
//
// Invariant: Instantiating Uses[id].Type() with Instances[id].TypeArgs
// results in an equivalent of Instances[id].Type.
Instances map[*ast.Ident]types.Instance

// Defs maps identifiers to the objects they define (including
// package names, dots "." of dot-imports, and blank "_" identifiers).
// For identifiers that do not denote objects (e.g., the package name
// in package clauses, or symbolic variables t in t := x.(type) of
// type switch headers), the corresponding objects are nil.
//
// For an embedded field, Defs returns the field *Var it defines.
//
// Invariant: Defs[id] == nil || Defs[id].Pos() == id.Pos()
Defs map[*ast.Ident]types.Object

// Uses maps identifiers to the objects they denote.
//
// For an embedded field, Uses returns the *TypeName it denotes.
//
// Invariant: Uses[id].Pos() != id.Pos()
Uses map[*ast.Ident]types.Object

// Implicits maps nodes to their implicitly declared objects, if any.
// The following node and object types may appear:
//
// node declared object
//
// *ast.ImportSpec *PkgName for imports without renames
// *ast.CaseClause type-specific *Var for each type switch case clause (incl. default)
// *ast.Field anonymous parameter *Var (incl. unnamed results)
//
Implicits map[ast.Node]types.Object

// Selections maps selector expressions (excluding qualified identifiers)
// to their corresponding selections.
Selections map[*ast.SelectorExpr]*types.Selection

// Scopes maps ast.Nodes to the scopes they define. Package scopes are not
// associated with a specific node but with all files belonging to a package.
// Thus, the package scope can be found in the type-checked Package object.
// Scopes nest, with the Universe scope being the outermost scope, enclosing
// the package scope, which contains (one or more) files scopes, which enclose
// function scopes which in turn enclose statement and function literal scopes.
// Note that even though package-level functions are declared in the package
// scope, the function scopes are embedded in the file scope of the file
// containing the function declaration.
//
// The following node types may appear in Scopes:
//
// *ast.File
// *ast.FuncType
// *ast.TypeSpec
// *ast.BlockStmt
// *ast.IfStmt
// *ast.SwitchStmt
// *ast.TypeSwitchStmt
// *ast.CaseClause
// *ast.CommClause
// *ast.ForStmt
// *ast.RangeStmt
//
Scopes map[ast.Node]*types.Scope

// InitOrder is the list of package-level initializers in the order in which
// they must be executed. Initializers referring to variables related by an
// initialization dependency appear in topological order, the others appear
// in source order. Variables without an initialization expression do not
// appear in this list.
// InitOrder []*Initializer
}

// ObjectOf returns the object denoted by the specified id,
// or nil if not found.
//
// If id is an embedded struct field, ObjectOf returns the field (*Var)
// it defines, not the type (*TypeName) it uses.
//
// Precondition: the Uses and Defs maps are populated.
func (info *Info) ObjectOf(id *ast.Ident) types.Object {
if obj := info.Defs[id]; obj != nil {
return obj
}
return info.Uses[id]
}

// TypeOf returns the type of expression e, or nil if not found.
// Precondition: the Types, Uses and Defs maps are populated.
func (info *Info) TypeOf(e ast.Expr) types.Type {
if t, ok := info.Types[e]; ok {
return t.Type
}
if id, _ := e.(*ast.Ident); id != nil {
if obj := info.ObjectOf(id); obj != nil {
return obj.Type()
}
}
return nil
}
type Info = typesutil.Info
19 changes: 14 additions & 5 deletions gopls/internal/lsp/cache/check.go
Original file line number Diff line number Diff line change
Expand Up @@ -673,7 +673,9 @@ func (b *typeCheckBatch) checkPackage(ctx context.Context, ph *packageHandle) (*
// TODO(rfindley): refactor to inline typeCheckImpl here. There is no need
// for so many layers to build up the package
// (checkPackage->typeCheckImpl->doTypeCheck).
pkg, err := typeCheckImpl(ctx, b, ph.localInputs)
// goxls: pass metadata to Go+ checker
// pkg, err := typeCheckImpl(ctx, b, ph.localInputs)
pkg, err := typeCheckImpl(ctx, b, ph)

if err == nil {
// Write package data to disk asynchronously.
Expand Down Expand Up @@ -1432,11 +1434,14 @@ func localPackageKey(inputs typeCheckInputs) source.Hash {
// typeCheckImpl type checks the parsed source files in compiledGoFiles.
// (The resulting pkg also holds the parsed but not type-checked goFiles.)
// deps holds the future results of type-checking the direct dependencies.
func typeCheckImpl(ctx context.Context, b *typeCheckBatch, inputs typeCheckInputs) (*syntaxPackage, error) {
// goxls: pass metadata to Go+ checker
// func typeCheckImpl(ctx context.Context, b *typeCheckBatch, inputs typeCheckInputs) (*syntaxPackage, error) {
func typeCheckImpl(ctx context.Context, b *typeCheckBatch, ph *packageHandle) (*syntaxPackage, error) {
inputs := ph.localInputs
ctx, done := event.Start(ctx, "cache.typeCheck", tag.Package.Of(string(inputs.id)))
defer done()

pkg, err := doTypeCheck(ctx, b, inputs)
pkg, err := doTypeCheck(ctx, b, ph)
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -1506,7 +1511,10 @@ func typeCheckImpl(ctx context.Context, b *typeCheckBatch, inputs typeCheckInput

var goVersionRx = regexp.MustCompile(`^go([1-9][0-9]*)\.(0|[1-9][0-9]*)$`)

func doTypeCheck(ctx context.Context, b *typeCheckBatch, inputs typeCheckInputs) (*syntaxPackage, error) {
// goxls: pass metadata to Go+ checker
// func doTypeCheck(ctx context.Context, b *typeCheckBatch, inputs typeCheckInputs) (*syntaxPackage, error) {
func doTypeCheck(ctx context.Context, b *typeCheckBatch, ph *packageHandle) (*syntaxPackage, error) {
inputs := ph.localInputs
pkg := &syntaxPackage{
id: inputs.id,
fset: b.fset, // must match parse call below
Expand Down Expand Up @@ -1579,7 +1587,8 @@ func doTypeCheck(ctx context.Context, b *typeCheckBatch, inputs typeCheckInputs)

// goxls: use Go+
// check := types.NewChecker(cfg, pkg.fset, pkg.types, pkg.typesInfo)
check := typesutil.NewChecker(cfg, pkg.fset, pkg.types, pkg.typesInfo, pkg.gopTypesInfo)
opts := &typesutil.Config{Types: pkg.types, Fset: pkg.fset, Mod: ph.m.GopMod_()}
check := typesutil.NewChecker(cfg, opts, pkg.typesInfo, pkg.gopTypesInfo)

var files []*ast.File
for _, cgf := range pkg.compiledGoFiles {
Expand Down
2 changes: 2 additions & 0 deletions gopls/internal/lsp/source/view.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import (
"io"

goxparser "github.com/goplus/gop/parser"
"github.com/goplus/mod/gopmod"
goximports "golang.org/x/tools/gopls/internal/goxls/imports"
"golang.org/x/tools/gopls/internal/goxls/typesutil"

Expand Down Expand Up @@ -563,6 +564,7 @@ type Metadata struct {
// goxls: Go+ files
GopFiles []span.URI
CompiledGopFiles []span.URI
gopMod_ *gopmod.Module // see GopMod_()

ForTest PackagePath // q in a "p [q.test]" package, else ""
TypesSizes types.Sizes
Expand Down
Loading