Skip to content
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
139 changes: 42 additions & 97 deletions decl.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package main

import (
"bytes"
"fmt"
"go/ast"
"go/token"
Expand Down Expand Up @@ -668,13 +667,53 @@ func lookup_types_ident(ident *ast.Ident, pos token.Pos, info *types.Info) types
return typ
}

func lookup_types_text(text string, typ types.Type) types.Type {
retry:
switch t := typ.(type) {
case *types.Array:
typ = t.Elem()
goto retry
case *types.Map:
typ = t.Elem()
goto retry
case *types.Pointer:
typ = t.Elem()
goto retry
case *types.Slice:
typ = t.Elem()
goto retry
case *types.Chan:
typ = t.Elem()
goto retry
case *types.Named:
if text == types.ExprString(toType(nil, typ)) {
return typ
}
typ = t.Underlying()
goto retry
case *types.Struct:
for i := 0; i < t.NumFields(); i++ {
if r := lookup_types_text(text, t.Field(i).Type()); r != nil {
return r
}
}
}
return nil
}

// lookup type by type, from type.
func lookup_types_expr(t ast.Expr, info *types.Info) types.Type {
text := types.ExprString(t)
for k, v := range info.Types {
if v.Type == nil {
continue
}
if text == types.ExprString(k) {
return v.Type
}
if t := lookup_types_text(text, v.Type); t != nil {
return t
}
}
return nil
}
Expand Down Expand Up @@ -703,8 +742,10 @@ func type_to_decl(t ast.Expr, scope *scope) *decl {
// }
} else if d.typeparams != nil {
// typeparams named type instance
retry:
if x, ok := t.(*ast.StarExpr); ok {
t = x.X
goto retry
}
if typ := g_daemon.autocomplete.lookup_types(t); typ != nil {
if named, ok := typ.(*types.Named); ok {
Expand Down Expand Up @@ -1289,102 +1330,6 @@ func get_array_len(e ast.Expr) string {
return ""
}

func pretty_print_type_expr(out io.Writer, e ast.Expr, canonical_aliases map[string]string) {
switch t := e.(type) {
case *ast.StarExpr:
fmt.Fprintf(out, "*")
pretty_print_type_expr(out, t.X, canonical_aliases)
case *ast.Ident:
if strings.HasPrefix(t.Name, "$") {
// beautify anonymous types
switch t.Name[1] {
case 's':
fmt.Fprintf(out, "struct")
case 'i':
// ok, in most cases anonymous interface is an
// empty interface, I'll just pretend that
// it's always true
fmt.Fprintf(out, "interface{}")
}
} else if !*g_debug && strings.HasPrefix(t.Name, "!") {
// these are full package names for disambiguating and pretty
// printing packages within packages, e.g.
// !go/ast!ast vs. !github.com/nsf/my/ast!ast
// another ugly hack, if people are punished in hell for ugly hacks
// I'm screwed...
emarkIdx := strings.LastIndex(t.Name, "!")
path := t.Name[1:emarkIdx]
alias := canonical_aliases[path]
if alias == "" {
alias = t.Name[emarkIdx+1:]
}
fmt.Fprintf(out, alias)
} else {
fmt.Fprintf(out, t.Name)
}
case *ast.ArrayType:
al := ""
if t.Len != nil {
al = get_array_len(t.Len)
}
if al != "" {
fmt.Fprintf(out, "[%s]", al)
} else {
fmt.Fprintf(out, "[]")
}
pretty_print_type_expr(out, t.Elt, canonical_aliases)
case *ast.SelectorExpr:
pretty_print_type_expr(out, t.X, canonical_aliases)
fmt.Fprintf(out, ".%s", t.Sel.Name)
case *ast.FuncType:
fmt.Fprintf(out, "func(")
pretty_print_func_field_list(out, t.Params, canonical_aliases)
fmt.Fprintf(out, ")")

buf := bytes.NewBuffer(make([]byte, 0, 256))
nresults := pretty_print_func_field_list(buf, t.Results, canonical_aliases)
if nresults > 0 {
results := buf.String()
if strings.IndexAny(results, ", ") != -1 {
results = "(" + results + ")"
}
fmt.Fprintf(out, " %s", results)
}
case *ast.MapType:
fmt.Fprintf(out, "map[")
pretty_print_type_expr(out, t.Key, canonical_aliases)
fmt.Fprintf(out, "]")
pretty_print_type_expr(out, t.Value, canonical_aliases)
case *ast.InterfaceType:
fmt.Fprintf(out, "interface{}")
case *ast.Ellipsis:
fmt.Fprintf(out, "...")
pretty_print_type_expr(out, t.Elt, canonical_aliases)
case *ast.StructType:
fmt.Fprintf(out, "struct")
case *ast.ChanType:
switch t.Dir {
case ast.RECV:
fmt.Fprintf(out, "<-chan ")
case ast.SEND:
fmt.Fprintf(out, "chan<- ")
case ast.SEND | ast.RECV:
fmt.Fprintf(out, "chan ")
}
pretty_print_type_expr(out, t.Value, canonical_aliases)
case *ast.ParenExpr:
fmt.Fprintf(out, "(")
pretty_print_type_expr(out, t.X, canonical_aliases)
fmt.Fprintf(out, ")")
case *ast.BadExpr:
// TODO: probably I should check that in a separate function
// and simply discard declarations with BadExpr as a part of their
// type
default:
// the element has some weird type, just ignore it
}
}

func pretty_print_func_field_list(out io.Writer, f *ast.FieldList, canonical_aliases map[string]string) int {
count := 0
if f == nil {
Expand Down
100 changes: 100 additions & 0 deletions types_go117.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,13 @@
package main

import (
"bytes"
"fmt"
"go/ast"
"go/token"
"go/types"
"io"
"strings"

pkgwalk "github.com/visualfc/gotools/types"
)
Expand Down Expand Up @@ -144,3 +148,99 @@ func DefaultPkgConfig() *pkgwalk.PkgConfig {
}
return conf
}

func pretty_print_type_expr(out io.Writer, e ast.Expr, canonical_aliases map[string]string) {
switch t := e.(type) {
case *ast.StarExpr:
fmt.Fprintf(out, "*")
pretty_print_type_expr(out, t.X, canonical_aliases)
case *ast.Ident:
if strings.HasPrefix(t.Name, "$") {
// beautify anonymous types
switch t.Name[1] {
case 's':
fmt.Fprintf(out, "struct")
case 'i':
// ok, in most cases anonymous interface is an
// empty interface, I'll just pretend that
// it's always true
fmt.Fprintf(out, "interface{}")
}
} else if !*g_debug && strings.HasPrefix(t.Name, "!") {
// these are full package names for disambiguating and pretty
// printing packages within packages, e.g.
// !go/ast!ast vs. !github.com/nsf/my/ast!ast
// another ugly hack, if people are punished in hell for ugly hacks
// I'm screwed...
emarkIdx := strings.LastIndex(t.Name, "!")
path := t.Name[1:emarkIdx]
alias := canonical_aliases[path]
if alias == "" {
alias = t.Name[emarkIdx+1:]
}
fmt.Fprintf(out, alias)
} else {
fmt.Fprintf(out, t.Name)
}
case *ast.ArrayType:
al := ""
if t.Len != nil {
al = get_array_len(t.Len)
}
if al != "" {
fmt.Fprintf(out, "[%s]", al)
} else {
fmt.Fprintf(out, "[]")
}
pretty_print_type_expr(out, t.Elt, canonical_aliases)
case *ast.SelectorExpr:
pretty_print_type_expr(out, t.X, canonical_aliases)
fmt.Fprintf(out, ".%s", t.Sel.Name)
case *ast.FuncType:
fmt.Fprintf(out, "func(")
pretty_print_func_field_list(out, t.Params, canonical_aliases)
fmt.Fprintf(out, ")")

buf := bytes.NewBuffer(make([]byte, 0, 256))
nresults := pretty_print_func_field_list(buf, t.Results, canonical_aliases)
if nresults > 0 {
results := buf.String()
if strings.IndexAny(results, ", ") != -1 {
results = "(" + results + ")"
}
fmt.Fprintf(out, " %s", results)
}
case *ast.MapType:
fmt.Fprintf(out, "map[")
pretty_print_type_expr(out, t.Key, canonical_aliases)
fmt.Fprintf(out, "]")
pretty_print_type_expr(out, t.Value, canonical_aliases)
case *ast.InterfaceType:
fmt.Fprintf(out, "interface{}")
case *ast.Ellipsis:
fmt.Fprintf(out, "...")
pretty_print_type_expr(out, t.Elt, canonical_aliases)
case *ast.StructType:
fmt.Fprintf(out, "struct")
case *ast.ChanType:
switch t.Dir {
case ast.RECV:
fmt.Fprintf(out, "<-chan ")
case ast.SEND:
fmt.Fprintf(out, "chan<- ")
case ast.SEND | ast.RECV:
fmt.Fprintf(out, "chan ")
}
pretty_print_type_expr(out, t.Value, canonical_aliases)
case *ast.ParenExpr:
fmt.Fprintf(out, "(")
pretty_print_type_expr(out, t.X, canonical_aliases)
fmt.Fprintf(out, ")")
case *ast.BadExpr:
// TODO: probably I should check that in a separate function
// and simply discard declarations with BadExpr as a part of their
// type
default:
// the element has some weird type, just ignore it
}
}
Loading