forked from encoredev/encore
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[ENC-307] Generic Response Types (encoredev#3)
This commit gets generic response types working under Go 1.18 and allows the system to return a response struct with a generic field within it. This commit also adds the ability for the encore parser to understand and obey `//go:build` tags, which helps with things like ARCH, OS, language level or tooling tags filters on files - preventing us trying to parse a file we can't or shouldn't.
- Loading branch information
Showing
17 changed files
with
759 additions
and
282 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,60 @@ | ||
//go:build go1.18 | ||
// +build go1.18 | ||
|
||
package parser | ||
|
||
import ( | ||
"go/ast" | ||
"reflect" | ||
|
||
"encr.dev/parser/est" | ||
schema "encr.dev/proto/encore/parser/schema/v1" | ||
) | ||
|
||
func init() { | ||
additionalTypeResolver = go118ResolveType | ||
} | ||
|
||
func go118ResolveType(p *parser, pkg *est.Package, file *est.File, expr ast.Expr) *schema.Type { | ||
switch expr := expr.(type) { | ||
// Needed for generic types with single generic parameters: `X[Index]` (i.e. `Vector[string]`) | ||
case *ast.IndexExpr: | ||
return resolveWithTypeArguments(p, pkg, file, expr.X, expr.Index) | ||
|
||
// Needed for generic types with multiple generic parameters: `X[A, B]` (i.e. `Skiplist[string, string]`) | ||
case *ast.IndexListExpr: | ||
return resolveWithTypeArguments(p, pkg, file, expr.X, expr.Indices...) | ||
} | ||
|
||
return nil | ||
} | ||
|
||
// resolveWithTypeArguments first resolves the parameterized declaration of `ident`, before resolving each of | ||
// the `typeArguments` to concrete types. It then returns a `*schema.Name` representing that instantiated type. | ||
func resolveWithTypeArguments(p *parser, pkg *est.Package, file *est.File, ident ast.Expr, typeArguments ...ast.Expr) *schema.Type { | ||
parameterizedType := p.resolveType(pkg, file, ident, nil) | ||
|
||
named := parameterizedType.GetNamed() | ||
if named == nil { | ||
p.errf(ident.Pos(), "expected to get a named type, got %+v", reflect.TypeOf(parameterizedType.Typ)) | ||
return parameterizedType | ||
} | ||
|
||
decl := p.decls[named.Id] | ||
if decl == nil { | ||
p.errf(ident.Pos(), "unable to find decl referenced") | ||
panic(bailout{}) | ||
} | ||
|
||
if len(decl.TypeParams) != len(typeArguments) { | ||
p.errf(ident.Pos(), "expected %d type parameters, got %d for reference to %s", len(decl.TypeParams), len(typeArguments), decl.Name) | ||
panic(bailout{}) | ||
} | ||
|
||
named.TypeArguments = make([]*schema.Type, len(decl.TypeParams)) | ||
for idx, expr := range typeArguments { | ||
named.TypeArguments[idx] = p.resolveType(pkg, file, expr, nil) | ||
} | ||
|
||
return parameterizedType | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
//go:build go1.16 && !go1.18 | ||
// +build go1.16,!go1.18 | ||
|
||
package parser | ||
|
||
import ( | ||
"go/ast" | ||
|
||
"encr.dev/parser/est" | ||
"encr.dev/parser/internal/names" | ||
schema "encr.dev/proto/encore/parser/schema/v1" | ||
) | ||
|
||
// parseDecl parses the type from a package declaration. | ||
func (p *parser) parseDecl(pkg *est.Package, d *names.PkgDecl, _ typeParameterLookup) *schema.Type { | ||
key := pkg.ImportPath + "." + d.Name | ||
decl, ok := p.declMap[key] | ||
if !ok { | ||
// We haven't parsed this yet; do so now. | ||
// Allocate a decl immediately so that we can properly handle | ||
// recursive types by short-circuiting above the second time we get here. | ||
id := uint32(len(p.decls)) | ||
typ := d.Spec.(*ast.TypeSpec).Type | ||
decl = &schema.Decl{ | ||
Id: id, | ||
Name: d.Name, | ||
Doc: d.Doc, | ||
TypeParams: nil, | ||
Loc: parseLoc(d.File, typ), | ||
// Type is set below | ||
} | ||
p.declMap[key] = decl | ||
p.decls = append(p.decls, decl) | ||
|
||
decl.Type = p.resolveType(pkg, d.File, d.Spec.(*ast.TypeSpec).Type, nil) | ||
} | ||
|
||
return &schema.Type{Typ: &schema.Type_Named{ | ||
Named: &schema.Named{Id: decl.Id}, | ||
}} | ||
} |
Oops, something went wrong.