5
0
mirror of https://github.com/wailsapp/wails.git synced 2025-05-03 04:11:05 +08:00

Support references to structs in other packages

This commit is contained in:
Lea Anthony 2023-02-24 21:15:39 +11:00
parent 8fd0e06c24
commit cd11c0a83c
4 changed files with 138 additions and 19 deletions

View File

@ -6,6 +6,7 @@ import (
"go/build" "go/build"
"go/parser" "go/parser"
"go/token" "go/token"
"log"
"os" "os"
"path/filepath" "path/filepath"
"strconv" "strconv"
@ -29,6 +30,7 @@ type ParameterType struct {
IsPointer bool IsPointer bool
MapKey *ParameterType MapKey *ParameterType
MapValue *ParameterType MapValue *ParameterType
Package string
} }
type Parameter struct { type Parameter struct {
@ -59,6 +61,7 @@ type ParsedPackage struct {
type Project struct { type Project struct {
Path string Path string
BoundMethods map[packagePath]map[structName][]*BoundMethod BoundMethods map[packagePath]map[structName][]*BoundMethod
Models map[packagePath]map[structName]*StructDef
} }
func ParseProject(projectPath string) (*Project, error) { func ParseProject(projectPath string) (*Project, error) {
@ -74,13 +77,19 @@ func ParseProject(projectPath string) (*Project, error) {
if err != nil { if err != nil {
return nil, err return nil, err
} }
for _, pkg := range packageCache {
if len(pkg.StructCache) > 0 {
if result.Models == nil {
result.Models = make(map[packagePath]map[structName]*StructDef)
}
result.Models[pkg.Path] = pkg.StructCache
}
}
return result, nil return result, nil
} }
func (p *Project) parseDirectory(dir string) (map[string]*ParsedPackage, error) { func (p *Project) parseDirectory(dir string) (map[string]*ParsedPackage, error) {
println("Parsing directory " + dir)
if packageCache[dir] != nil { if packageCache[dir] != nil {
println("Found directory in cache!")
return map[string]*ParsedPackage{dir: packageCache[dir]}, nil return map[string]*ParsedPackage{dir: packageCache[dir]}, nil
} }
// Parse the directory // Parse the directory
@ -105,7 +114,7 @@ func (p *Project) parseDirectory(dir string) (map[string]*ParsedPackage, error)
Dir: getDirectoryForPackage(pkg), Dir: getDirectoryForPackage(pkg),
StructCache: make(map[structName]*StructDef), StructCache: make(map[structName]*StructDef),
} }
packageCache[dir] = parsedPackage packageCache[packageName] = parsedPackage
result[packageName] = parsedPackage result[packageName] = parsedPackage
} }
return result, nil return result, nil
@ -115,9 +124,8 @@ func (p *Project) findApplicationNewCalls(pkgs map[string]*ParsedPackage) (err e
var callFound bool var callFound bool
for packageName, pkg := range pkgs { for _, pkg := range pkgs {
thisPackage := pkg.Pkg thisPackage := pkg.Pkg
println(" - Looking in package: " + packageName)
// Iterate through the package's files // Iterate through the package's files
for _, file := range thisPackage.Files { for _, file := range thisPackage.Files {
// Use an ast.Inspector to find the calls to application.New // Use an ast.Inspector to find the calls to application.New
@ -338,12 +346,20 @@ func (p *Project) parseParameterType(field *ast.Field, pkg *ParsedPackage) *Para
result := &ParameterType{} result := &ParameterType{}
result.Name = getTypeString(field.Type) result.Name = getTypeString(field.Type)
switch t := field.Type.(type) { switch t := field.Type.(type) {
case *ast.Ident:
result.IsStruct = isStructType(t)
case *ast.StarExpr: case *ast.StarExpr:
result = p.parseParameterType(&ast.Field{Type: t.X}, pkg) result = p.parseParameterType(&ast.Field{Type: t.X}, pkg)
result.IsPointer = true result.IsPointer = true
result.IsStruct = isStructType(t.X)
case *ast.StructType: case *ast.StructType:
result.IsStruct = true result.IsStruct = true
case *ast.SelectorExpr:
extPackage, err := p.getParsedPackageFromName(t.X.(*ast.Ident).Name, pkg)
if err != nil {
log.Fatal(err)
}
result.IsStruct = p.getStructDef(t.Sel.Name, extPackage)
result.Package = extPackage.Path
case *ast.ArrayType: case *ast.ArrayType:
result.IsSlice = true result.IsSlice = true
result.IsStruct = isStructType(t.Elt) result.IsStruct = isStructType(t.Elt)
@ -355,18 +371,18 @@ func (p *Project) parseParameterType(field *ast.Field, pkg *ParsedPackage) *Para
default: default:
} }
if result.IsStruct { if result.IsStruct {
_, ok := pkg.StructCache[result.Name]
if !ok {
p.getStructDef(result.Name, pkg) p.getStructDef(result.Name, pkg)
if result.Package == "" {
result.Package = pkg.Path
} }
} }
return result return result
} }
func (p *Project) getStructDef(name string, pkg *ParsedPackage) { func (p *Project) getStructDef(name string, pkg *ParsedPackage) bool {
_, ok := pkg.StructCache[name] _, ok := pkg.StructCache[name]
if ok { if ok {
return return true
} }
// Iterate over all files in the package // Iterate over all files in the package
for _, file := range pkg.Pkg.Files { for _, file := range pkg.Pkg.Files {
@ -386,6 +402,7 @@ func (p *Project) getStructDef(name string, pkg *ParsedPackage) {
} }
pkg.StructCache[name] = result pkg.StructCache[name] = result
result.Fields = p.parseStructFields(structType, pkg) result.Fields = p.parseStructFields(structType, pkg)
return true
} }
} }
} }
@ -394,6 +411,7 @@ func (p *Project) getStructDef(name string, pkg *ParsedPackage) {
} }
} }
} }
return false
} }
func (p *Project) parseStructFields(structType *ast.StructType, pkg *ParsedPackage) []*Field { func (p *Project) parseStructFields(structType *ast.StructType, pkg *ParsedPackage) []*Field {
@ -419,6 +437,9 @@ func (p *Project) parseStructFields(structType *ast.StructType, pkg *ParsedPacka
if !ok { if !ok {
p.getStructDef(paramType.Name, pkg) p.getStructDef(paramType.Name, pkg)
} }
if paramType.Package == "" {
paramType.Package = pkg.Path
}
} }
thisField.Type = paramType thisField.Type = paramType
result = append(result, thisField) result = append(result, thisField)
@ -445,13 +466,15 @@ func (p *Project) getParsedPackageFromName(packageName string, currentPackage *P
if err != nil { if err != nil {
return nil, err return nil, err
} }
return &ParsedPackage{ result := &ParsedPackage{
Pkg: pkg, Pkg: pkg,
Name: packageName, Name: packageName,
Path: path, Path: path,
Dir: dir, Dir: dir,
StructCache: make(map[string]*StructDef), StructCache: make(map[string]*StructDef),
}, nil }
packageCache[path] = result
return result, nil
} }
} }
} }
@ -490,6 +513,8 @@ func getTypeString(expr ast.Expr) string {
return getTypeString(t.Elt) return getTypeString(t.Elt)
case *ast.MapType: case *ast.MapType:
return "map" return "map"
case *ast.SelectorExpr:
return getTypeString(t.Sel)
default: default:
return "any" return "any"
} }

View File

@ -11,6 +11,7 @@ func TestParseDirectory(t *testing.T) {
name string name string
dir string dir string
wantBoundMethods map[string]map[string][]*BoundMethod wantBoundMethods map[string]map[string][]*BoundMethod
wantModels map[string]map[string]*StructDef
wantErr bool wantErr bool
}{ }{
{ {
@ -689,6 +690,7 @@ func TestParseDirectory(t *testing.T) {
Name: "Person", Name: "Person",
IsPointer: true, IsPointer: true,
IsStruct: true, IsStruct: true,
Package: "main",
}, },
}, },
}, },
@ -709,6 +711,7 @@ func TestParseDirectory(t *testing.T) {
Name: "Person", Name: "Person",
IsPointer: true, IsPointer: true,
IsStruct: true, IsStruct: true,
Package: "main",
}, },
}, },
}, },
@ -718,6 +721,7 @@ func TestParseDirectory(t *testing.T) {
Name: "Person", Name: "Person",
IsPointer: true, IsPointer: true,
IsStruct: true, IsStruct: true,
Package: "main",
}, },
}, },
}, },
@ -841,6 +845,30 @@ func TestParseDirectory(t *testing.T) {
}, },
}, },
}, },
wantModels: map[string]map[string]*StructDef{
"main": {
"Person": {
Name: "Person",
Fields: []*Field{
{
Name: "Name",
Type: &ParameterType{
Name: "string",
},
},
{
Name: "Parent",
Type: &ParameterType{
Name: "Person",
IsStruct: true,
IsPointer: true,
Package: "main",
},
},
},
},
},
},
wantErr: false, wantErr: false,
}, },
{ {
@ -960,6 +988,7 @@ func TestParseDirectory(t *testing.T) {
Name: "Person", Name: "Person",
IsPointer: true, IsPointer: true,
IsStruct: true, IsStruct: true,
Package: "main",
}, },
}, },
}, },
@ -973,8 +1002,10 @@ func TestParseDirectory(t *testing.T) {
Outputs: []*Parameter{ Outputs: []*Parameter{
{ {
Type: &ParameterType{ Type: &ParameterType{
Name: "int", Name: "Address",
IsSlice: true, IsStruct: true,
IsPointer: true,
Package: "github.com/wailsapp/wails/v3/internal/parser/testdata/struct_literal_multiple_other/services",
}, },
}, },
}, },
@ -982,6 +1013,55 @@ func TestParseDirectory(t *testing.T) {
}, },
}, },
}, },
wantModels: map[string]map[string]*StructDef{
"main": {
"Person": {
Name: "Person",
Fields: []*Field{
{
Name: "Name",
Type: &ParameterType{
Name: "string",
},
},
{
Name: "Address",
Type: &ParameterType{
Name: "Address",
IsStruct: true,
IsPointer: true,
Package: "github.com/wailsapp/wails/v3/internal/parser/testdata/struct_literal_multiple_other/services",
},
},
},
},
},
"github.com/wailsapp/wails/v3/internal/parser/testdata/struct_literal_multiple_other/services": {
"Address": {
Name: "Address",
Fields: []*Field{
{
Name: "Street",
Type: &ParameterType{
Name: "string",
},
},
{
Name: "State",
Type: &ParameterType{
Name: "string",
},
},
{
Name: "Country",
Type: &ParameterType{
Name: "string",
},
},
},
},
},
},
}, },
} }
for _, tt := range tests { for _, tt := range tests {
@ -994,6 +1074,9 @@ func TestParseDirectory(t *testing.T) {
if diff := cmp.Diff(tt.wantBoundMethods, got.BoundMethods); diff != "" { if diff := cmp.Diff(tt.wantBoundMethods, got.BoundMethods); diff != "" {
t.Errorf("ParseDirectory() failed:\n" + diff) t.Errorf("ParseDirectory() failed:\n" + diff)
} }
if diff := cmp.Diff(tt.wantModels, got.Models); diff != "" {
t.Errorf("ParseDirectory() failed:\n" + diff)
}
}) })
} }

View File

@ -17,6 +17,7 @@ type GreetService struct {
type Person struct { type Person struct {
Name string Name string
Address *services.Address
} }
// Greet does XYZ // Greet does XYZ

View File

@ -6,7 +6,17 @@ type OtherService struct {
t int t int
} }
// Yay does this and that type Address struct {
func (o *OtherService) Yay() []int { Street string
return []int{0, 1, 2} State string
Country string
}
// Yay does this and that
func (o *OtherService) Yay() *Address {
return &Address{
Street: "123 Pitt Street",
State: "New South Wales",
Country: "Australia",
}
} }