Skip to content

Commit

Permalink
Publish typecheck diagnostics
Browse files Browse the repository at this point in the history
  • Loading branch information
harry-hov committed Mar 26, 2024
1 parent fe1aefc commit afec8a8
Show file tree
Hide file tree
Showing 5 changed files with 101 additions and 29 deletions.
5 changes: 5 additions & 0 deletions internal/env/env.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,11 @@ import (
"path/filepath"
)

var GlobalEnv = &Env{
GNOROOT: "",
GNOHOME: "",
}

type Env struct {
GNOROOT string
GNOHOME string
Expand Down
20 changes: 18 additions & 2 deletions internal/lsp/cache.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,24 @@ func NewCache() *Cache {
}

func (s *server) UpdateCache(pkgPath string) {
// TODO: Unify `GetPackageInfo()` and `PackageFromDir()`?
pkg, err := PackageFromDir(pkgPath, false)
if err == nil {
s.cache.pkgs.Set(pkgPath, pkg)
if err != nil {
return
}
pkginfo, err := GetPackageInfo(pkgPath)
if err != nil {
return
}

tc, errs := NewTypeCheck()
tc.cfg.Importer = tc // set typeCheck importer
res := pkginfo.TypeCheck(tc)

// Mutate `res.err` with `errs`, as `res.err` contains
// only the first error found.
res.err = *errs

pkg.TypeCheckResult = res // set typeCheck result
s.cache.pkgs.Set(pkgPath, pkg)
}
86 changes: 65 additions & 21 deletions internal/lsp/check.go
Original file line number Diff line number Diff line change
@@ -1,16 +1,21 @@
package lsp

import (
"errors"
"fmt"
"go/ast"
"go/parser"
"go/token"
"go/types"
"log/slog"
"math"
"os"
"path/filepath"
"strconv"
"strings"

"github.com/gnolang/gno/gnovm/pkg/gnomod"
"github.com/harry-hov/gnopls/internal/env"
"go.uber.org/multierr"
)

Expand All @@ -28,14 +33,22 @@ type PackageGetter interface {
GetPackageInfo(path string) *PackageInfo
}

// GetPackageInfo accepts path(abs) or importpath and returns
// PackageInfo if found.
// Note: it doesn't work for relative path
func GetPackageInfo(path string) (*PackageInfo, error) {
// TODO: fix
if strings.HasPrefix(path, "/") {
// No op
} else if strings.HasPrefix(path, "gno.land/") {
path = "/Users/harry/Desktop/work/gno/examples/" + path
} else {
path = "/Users/harry/Desktop/work/gno/gnovm/stdlibs/" + path
// if not absolute, assume its import path
if !filepath.IsAbs(path) {
if env.GlobalEnv.GNOROOT == "" {
// if GNOROOT is unknown, we can't locate the
// `examples` and `stdlibs`
return nil, errors.New("GNOROOT not set")
}
if strings.HasPrefix(path, "gno.land/") { // look in `examples`
path = filepath.Join(env.GlobalEnv.GNOROOT, "examples", path)
} else { // look into `stdlibs`
path = filepath.Join(env.GlobalEnv.GNOROOT, "gnovm", "stdlibs", path)
}
}
return getPackageInfo(path)
}
Expand Down Expand Up @@ -76,26 +89,28 @@ func getPackageInfo(path string) (*PackageInfo, error) {
}, nil
}

type TypeCheckResult struct {
pkg *types.Package
fset *token.FileSet
files []*ast.File
info *types.Info
err error
}

type TypeCheck struct {
cache map[string]*TypeCheckResult
cfg *types.Config
}

// Unused, but satisfies the Importer interface.
func NewTypeCheck() (*TypeCheck, *error) {
var errs error
return &TypeCheck{
cache: map[string]*TypeCheckResult{},
cfg: &types.Config{
Error: func(err error) {
errs = multierr.Append(errs, err)
},
},
}, &errs
}

func (tc *TypeCheck) Import(path string) (*types.Package, error) {
return tc.ImportFrom(path, "", 0)
}

// ImportFrom returns the imported package for the given import
// path when imported by a package file located in dir.
// ImportFrom returns the imported package for the given import path
func (tc *TypeCheck) ImportFrom(path, _ string, _ types.ImportMode) (*types.Package, error) {
if pkg, ok := tc.cache[path]; ok {
return pkg.pkg, pkg.err
Expand Down Expand Up @@ -136,8 +151,37 @@ func (pi *PackageInfo) TypeCheck(tc *TypeCheck) *TypeCheckResult {
files = append(files, pgf)
}
pkg, err := tc.cfg.Check(pi.ImportPath, fset, files, info)
if err != nil {
return &TypeCheckResult{err: err}
return &TypeCheckResult{pkg: pkg, fset: fset, files: files, info: info, err: err}
}

type TypeCheckResult struct {
pkg *types.Package
fset *token.FileSet
files []*ast.File
info *types.Info
err error
}

func (tcr *TypeCheckResult) Errors() []ErrorInfo {
errs := multierr.Errors(tcr.err)
res := make([]ErrorInfo, 0, len(errs))
for _, err := range errs {
parts := strings.Split(err.Error(), ":")
if len(parts) < 4 {
slog.Error("TYPECHECK", "skipped", err)
}
filename := strings.TrimSpace(parts[0])
line, _ := strconv.Atoi(strings.TrimSpace(parts[1]))
col, _ := strconv.Atoi(strings.TrimSpace(parts[2]))
msg := strings.TrimSpace(strings.Join(parts[3:], ":"))
res = append(res, ErrorInfo{
FileName: filename,
Line: line,
Column: col,
Span: []int{col, math.MaxInt},
Msg: msg,
Tool: "go/typecheck",
})
}
return &TypeCheckResult{pkg: pkg, fset: fset, files: files, info: info, err: nil}
return res
}
7 changes: 7 additions & 0 deletions internal/lsp/diagnostics.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,13 @@ func (s *server) publishDiagnostics(ctx context.Context, conn jsonrpc2.Conn, fil
if err != nil {
return err
}
pkg, ok := s.cache.pkgs.Get(filepath.Dir(string(file.URI.Filename())))
if ok {
errs := pkg.TypeCheckResult.Errors()
if errs != nil {
errors = append(errors, errs...)
}
}

mPublishDiagnosticParams := make(map[string]*protocol.PublishDiagnosticsParams)
publishDiagnosticParams := make([]*protocol.PublishDiagnosticsParams, 0)
Expand Down
12 changes: 6 additions & 6 deletions internal/lsp/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,24 +26,24 @@ type server struct {
formatOpt tools.FormattingOption
}

func BuildServerHandler(conn jsonrpc2.Conn, env *env.Env) jsonrpc2.Handler {
func BuildServerHandler(conn jsonrpc2.Conn, e *env.Env) jsonrpc2.Handler {
dirs := []string{}
if env.GNOROOT != "" {
dirs = append(dirs, filepath.Join(env.GNOROOT, "examples"))
dirs = append(dirs, filepath.Join(env.GNOROOT, "gnovm/stdlibs"))
if e.GNOROOT != "" {
dirs = append(dirs, filepath.Join(e.GNOROOT, "examples"))
dirs = append(dirs, filepath.Join(e.GNOROOT, "gnovm/stdlibs"))
}
server := &server{
conn: conn,

env: env,
env: e,

snapshot: NewSnapshot(),
completionStore: InitCompletionStore(dirs),
cache: NewCache(),

formatOpt: tools.Gofumpt,
}

env.GlobalEnv = e
return jsonrpc2.ReplyHandler(server.ServerHandler)
}

Expand Down

0 comments on commit afec8a8

Please sign in to comment.