Skip to content

Commit

Permalink
Merge pull request #25 from xushiwei/q
Browse files Browse the repository at this point in the history
typesutil.Check
  • Loading branch information
xushiwei authored Oct 20, 2023
2 parents 0babbc8 + a9757b1 commit 1d2fb60
Show file tree
Hide file tree
Showing 8 changed files with 104 additions and 173 deletions.
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

0 comments on commit 1d2fb60

Please sign in to comment.