5
0
mirror of https://github.com/wailsapp/wails.git synced 2025-05-02 03:01:45 +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/parser"
"go/token"
"log"
"os"
"path/filepath"
"strconv"
@ -29,6 +30,7 @@ type ParameterType struct {
IsPointer bool
MapKey *ParameterType
MapValue *ParameterType
Package string
}
type Parameter struct {
@ -59,6 +61,7 @@ type ParsedPackage struct {
type Project struct {
Path string
BoundMethods map[packagePath]map[structName][]*BoundMethod
Models map[packagePath]map[structName]*StructDef
}
func ParseProject(projectPath string) (*Project, error) {
@ -74,13 +77,19 @@ func ParseProject(projectPath string) (*Project, error) {
if err != nil {
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
}
func (p *Project) parseDirectory(dir string) (map[string]*ParsedPackage, error) {
println("Parsing directory " + dir)
if packageCache[dir] != nil {
println("Found directory in cache!")
return map[string]*ParsedPackage{dir: packageCache[dir]}, nil
}
// Parse the directory
@ -105,7 +114,7 @@ func (p *Project) parseDirectory(dir string) (map[string]*ParsedPackage, error)
Dir: getDirectoryForPackage(pkg),
StructCache: make(map[structName]*StructDef),
}
packageCache[dir] = parsedPackage
packageCache[packageName] = parsedPackage
result[packageName] = parsedPackage
}
return result, nil
@ -115,9 +124,8 @@ func (p *Project) findApplicationNewCalls(pkgs map[string]*ParsedPackage) (err e
var callFound bool
for packageName, pkg := range pkgs {
for _, pkg := range pkgs {
thisPackage := pkg.Pkg
println(" - Looking in package: " + packageName)
// Iterate through the package's files
for _, file := range thisPackage.Files {
// 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.Name = getTypeString(field.Type)
switch t := field.Type.(type) {
case *ast.Ident:
result.IsStruct = isStructType(t)
case *ast.StarExpr:
result = p.parseParameterType(&ast.Field{Type: t.X}, pkg)
result.IsPointer = true
result.IsStruct = isStructType(t.X)
case *ast.StructType:
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:
result.IsSlice = true
result.IsStruct = isStructType(t.Elt)
@ -355,18 +371,18 @@ func (p *Project) parseParameterType(field *ast.Field, pkg *ParsedPackage) *Para
default:
}
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
}
func (p *Project) getStructDef(name string, pkg *ParsedPackage) {
func (p *Project) getStructDef(name string, pkg *ParsedPackage) bool {
_, ok := pkg.StructCache[name]
if ok {
return
return true
}
// Iterate over all files in the package
for _, file := range pkg.Pkg.Files {
@ -386,6 +402,7 @@ func (p *Project) getStructDef(name string, pkg *ParsedPackage) {
}
pkg.StructCache[name] = result
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 {
@ -419,6 +437,9 @@ func (p *Project) parseStructFields(structType *ast.StructType, pkg *ParsedPacka
if !ok {
p.getStructDef(paramType.Name, pkg)
}
if paramType.Package == "" {
paramType.Package = pkg.Path
}
}
thisField.Type = paramType
result = append(result, thisField)
@ -445,13 +466,15 @@ func (p *Project) getParsedPackageFromName(packageName string, currentPackage *P
if err != nil {
return nil, err
}
return &ParsedPackage{
result := &ParsedPackage{
Pkg: pkg,
Name: packageName,
Path: path,
Dir: dir,
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)
case *ast.MapType:
return "map"
case *ast.SelectorExpr:
return getTypeString(t.Sel)
default:
return "any"
}

View File

@ -11,6 +11,7 @@ func TestParseDirectory(t *testing.T) {
name string
dir string
wantBoundMethods map[string]map[string][]*BoundMethod
wantModels map[string]map[string]*StructDef
wantErr bool
}{
{
@ -689,6 +690,7 @@ func TestParseDirectory(t *testing.T) {
Name: "Person",
IsPointer: true,
IsStruct: true,
Package: "main",
},
},
},
@ -709,6 +711,7 @@ func TestParseDirectory(t *testing.T) {
Name: "Person",
IsPointer: true,
IsStruct: true,
Package: "main",
},
},
},
@ -718,6 +721,7 @@ func TestParseDirectory(t *testing.T) {
Name: "Person",
IsPointer: 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,
},
{
@ -960,6 +988,7 @@ func TestParseDirectory(t *testing.T) {
Name: "Person",
IsPointer: true,
IsStruct: true,
Package: "main",
},
},
},
@ -973,8 +1002,10 @@ func TestParseDirectory(t *testing.T) {
Outputs: []*Parameter{
{
Type: &ParameterType{
Name: "int",
IsSlice: true,
Name: "Address",
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 {
@ -994,6 +1074,9 @@ func TestParseDirectory(t *testing.T) {
if diff := cmp.Diff(tt.wantBoundMethods, got.BoundMethods); diff != "" {
t.Errorf("ParseDirectory() failed:\n" + diff)
}
if diff := cmp.Diff(tt.wantModels, got.Models); diff != "" {
t.Errorf("ParseDirectory() failed:\n" + diff)
}
})
}

View File

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

View File

@ -6,7 +6,17 @@ type OtherService struct {
t int
}
// Yay does this and that
func (o *OtherService) Yay() []int {
return []int{0, 1, 2}
type Address struct {
Street string
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",
}
}