mirror of
https://github.com/tailscale/tailscale.git
synced 2025-10-29 07:09:33 +00:00
cmd/viewer: add field comments to generated view methods
Extract field comments from AST and include them in generated view methods. Comments are preserved from the original struct fields to provide documentation for the view accessors. Fixes #16958 Signed-off-by: Maisem Ali <3953239+maisem@users.noreply.github.com>
This commit is contained in:
committed by
Brad Fitzpatrick
parent
80f5a00e76
commit
882b05fff9
@@ -247,47 +247,41 @@ func (v *MapView) UnmarshalJSONFrom(dec *jsontext.Decoder) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (v MapView) Int() views.Map[string, int] { return views.MapOf(v.ж.Int) }
|
||||
|
||||
func (v MapView) Int() views.Map[string, int] { return views.MapOf(v.ж.Int) }
|
||||
func (v MapView) SliceInt() views.MapSlice[string, int] { return views.MapSliceOf(v.ж.SliceInt) }
|
||||
|
||||
func (v MapView) StructPtrWithPtr() views.MapFn[string, *StructWithPtrs, StructWithPtrsView] {
|
||||
return views.MapFnOf(v.ж.StructPtrWithPtr, func(t *StructWithPtrs) StructWithPtrsView {
|
||||
return t.View()
|
||||
})
|
||||
}
|
||||
|
||||
func (v MapView) StructPtrWithoutPtr() views.MapFn[string, *StructWithoutPtrs, StructWithoutPtrsView] {
|
||||
return views.MapFnOf(v.ж.StructPtrWithoutPtr, func(t *StructWithoutPtrs) StructWithoutPtrsView {
|
||||
return t.View()
|
||||
})
|
||||
}
|
||||
|
||||
func (v MapView) StructWithoutPtr() views.Map[string, StructWithoutPtrs] {
|
||||
return views.MapOf(v.ж.StructWithoutPtr)
|
||||
}
|
||||
|
||||
func (v MapView) SlicesWithPtrs() views.MapFn[string, []*StructWithPtrs, views.SliceView[*StructWithPtrs, StructWithPtrsView]] {
|
||||
return views.MapFnOf(v.ж.SlicesWithPtrs, func(t []*StructWithPtrs) views.SliceView[*StructWithPtrs, StructWithPtrsView] {
|
||||
return views.SliceOfViews[*StructWithPtrs, StructWithPtrsView](t)
|
||||
})
|
||||
}
|
||||
|
||||
func (v MapView) SlicesWithoutPtrs() views.MapFn[string, []*StructWithoutPtrs, views.SliceView[*StructWithoutPtrs, StructWithoutPtrsView]] {
|
||||
return views.MapFnOf(v.ж.SlicesWithoutPtrs, func(t []*StructWithoutPtrs) views.SliceView[*StructWithoutPtrs, StructWithoutPtrsView] {
|
||||
return views.SliceOfViews[*StructWithoutPtrs, StructWithoutPtrsView](t)
|
||||
})
|
||||
}
|
||||
|
||||
func (v MapView) StructWithoutPtrKey() views.Map[StructWithoutPtrs, int] {
|
||||
return views.MapOf(v.ж.StructWithoutPtrKey)
|
||||
}
|
||||
|
||||
func (v MapView) StructWithPtr() views.MapFn[string, StructWithPtrs, StructWithPtrsView] {
|
||||
return views.MapFnOf(v.ж.StructWithPtr, func(t StructWithPtrs) StructWithPtrsView {
|
||||
return t.View()
|
||||
})
|
||||
}
|
||||
|
||||
// Unsupported views.
|
||||
func (v MapView) SliceIntPtr() map[string][]*int { panic("unsupported") }
|
||||
func (v MapView) PointerKey() map[*string]int { panic("unsupported") }
|
||||
func (v MapView) StructWithPtrKey() map[StructWithPtrs]int { panic("unsupported") }
|
||||
@@ -389,8 +383,10 @@ func (v StructWithSlicesView) Prefixes() views.Slice[netip.Prefix] {
|
||||
return views.SliceOf(v.ж.Prefixes)
|
||||
}
|
||||
func (v StructWithSlicesView) Data() views.ByteSlice[[]byte] { return views.ByteSliceOf(v.ж.Data) }
|
||||
func (v StructWithSlicesView) Structs() StructWithPtrs { panic("unsupported") }
|
||||
func (v StructWithSlicesView) Ints() *int { panic("unsupported") }
|
||||
|
||||
// Unsupported views.
|
||||
func (v StructWithSlicesView) Structs() StructWithPtrs { panic("unsupported") }
|
||||
func (v StructWithSlicesView) Ints() *int { panic("unsupported") }
|
||||
|
||||
// A compilation failure here means this code must be regenerated, with the command at the top of this file.
|
||||
var _StructWithSlicesViewNeedsRegeneration = StructWithSlices(struct {
|
||||
@@ -554,9 +550,10 @@ func (v GenericIntStructView[T]) Pointer() views.ValuePointer[T] {
|
||||
return views.ValuePointerOf(v.ж.Pointer)
|
||||
}
|
||||
|
||||
func (v GenericIntStructView[T]) Slice() views.Slice[T] { return views.SliceOf(v.ж.Slice) }
|
||||
func (v GenericIntStructView[T]) Slice() views.Slice[T] { return views.SliceOf(v.ж.Slice) }
|
||||
func (v GenericIntStructView[T]) Map() views.Map[string, T] { return views.MapOf(v.ж.Map) }
|
||||
|
||||
func (v GenericIntStructView[T]) Map() views.Map[string, T] { return views.MapOf(v.ж.Map) }
|
||||
// Unsupported views.
|
||||
func (v GenericIntStructView[T]) PtrSlice() *T { panic("unsupported") }
|
||||
func (v GenericIntStructView[T]) PtrKeyMap() map[*T]string { panic("unsupported") }
|
||||
func (v GenericIntStructView[T]) PtrValueMap() map[string]*T { panic("unsupported") }
|
||||
@@ -648,9 +645,10 @@ func (v GenericNoPtrsStructView[T]) Pointer() views.ValuePointer[T] {
|
||||
return views.ValuePointerOf(v.ж.Pointer)
|
||||
}
|
||||
|
||||
func (v GenericNoPtrsStructView[T]) Slice() views.Slice[T] { return views.SliceOf(v.ж.Slice) }
|
||||
func (v GenericNoPtrsStructView[T]) Slice() views.Slice[T] { return views.SliceOf(v.ж.Slice) }
|
||||
func (v GenericNoPtrsStructView[T]) Map() views.Map[string, T] { return views.MapOf(v.ж.Map) }
|
||||
|
||||
func (v GenericNoPtrsStructView[T]) Map() views.Map[string, T] { return views.MapOf(v.ж.Map) }
|
||||
// Unsupported views.
|
||||
func (v GenericNoPtrsStructView[T]) PtrSlice() *T { panic("unsupported") }
|
||||
func (v GenericNoPtrsStructView[T]) PtrKeyMap() map[*T]string { panic("unsupported") }
|
||||
func (v GenericNoPtrsStructView[T]) PtrValueMap() map[string]*T { panic("unsupported") }
|
||||
@@ -741,12 +739,13 @@ func (v GenericCloneableStructView[T, V]) Value() V { return v.ж.Value.View() }
|
||||
func (v GenericCloneableStructView[T, V]) Slice() views.SliceView[T, V] {
|
||||
return views.SliceOfViews[T, V](v.ж.Slice)
|
||||
}
|
||||
|
||||
func (v GenericCloneableStructView[T, V]) Map() views.MapFn[string, T, V] {
|
||||
return views.MapFnOf(v.ж.Map, func(t T) V {
|
||||
return t.View()
|
||||
})
|
||||
}
|
||||
|
||||
// Unsupported views.
|
||||
func (v GenericCloneableStructView[T, V]) Pointer() map[string]T { panic("unsupported") }
|
||||
func (v GenericCloneableStructView[T, V]) PtrSlice() *T { panic("unsupported") }
|
||||
func (v GenericCloneableStructView[T, V]) PtrKeyMap() map[*T]string { panic("unsupported") }
|
||||
@@ -942,25 +941,21 @@ func (v StructWithTypeAliasFieldsView) SliceWithPtrs() views.SliceView[*StructWi
|
||||
func (v StructWithTypeAliasFieldsView) SliceWithoutPtrs() views.SliceView[*StructWithoutPtrsAlias, StructWithoutPtrsAliasView] {
|
||||
return views.SliceOfViews[*StructWithoutPtrsAlias, StructWithoutPtrsAliasView](v.ж.SliceWithoutPtrs)
|
||||
}
|
||||
|
||||
func (v StructWithTypeAliasFieldsView) MapWithPtrs() views.MapFn[string, *StructWithPtrsAlias, StructWithPtrsAliasView] {
|
||||
return views.MapFnOf(v.ж.MapWithPtrs, func(t *StructWithPtrsAlias) StructWithPtrsAliasView {
|
||||
return t.View()
|
||||
})
|
||||
}
|
||||
|
||||
func (v StructWithTypeAliasFieldsView) MapWithoutPtrs() views.MapFn[string, *StructWithoutPtrsAlias, StructWithoutPtrsAliasView] {
|
||||
return views.MapFnOf(v.ж.MapWithoutPtrs, func(t *StructWithoutPtrsAlias) StructWithoutPtrsAliasView {
|
||||
return t.View()
|
||||
})
|
||||
}
|
||||
|
||||
func (v StructWithTypeAliasFieldsView) MapOfSlicesWithPtrs() views.MapFn[string, []*StructWithPtrsAlias, views.SliceView[*StructWithPtrsAlias, StructWithPtrsAliasView]] {
|
||||
return views.MapFnOf(v.ж.MapOfSlicesWithPtrs, func(t []*StructWithPtrsAlias) views.SliceView[*StructWithPtrsAlias, StructWithPtrsAliasView] {
|
||||
return views.SliceOfViews[*StructWithPtrsAlias, StructWithPtrsAliasView](t)
|
||||
})
|
||||
}
|
||||
|
||||
func (v StructWithTypeAliasFieldsView) MapOfSlicesWithoutPtrs() views.MapFn[string, []*StructWithoutPtrsAlias, views.SliceView[*StructWithoutPtrsAlias, StructWithoutPtrsAliasView]] {
|
||||
return views.MapFnOf(v.ж.MapOfSlicesWithoutPtrs, func(t []*StructWithoutPtrsAlias) views.SliceView[*StructWithoutPtrsAlias, StructWithoutPtrsAliasView] {
|
||||
return views.SliceOfViews[*StructWithoutPtrsAlias, StructWithoutPtrsAliasView](t)
|
||||
|
||||
@@ -9,6 +9,8 @@ import (
|
||||
"bytes"
|
||||
"flag"
|
||||
"fmt"
|
||||
"go/ast"
|
||||
"go/token"
|
||||
"go/types"
|
||||
"html/template"
|
||||
"log"
|
||||
@@ -17,6 +19,7 @@ import (
|
||||
"strings"
|
||||
|
||||
"tailscale.com/util/codegen"
|
||||
"tailscale.com/util/mak"
|
||||
"tailscale.com/util/must"
|
||||
)
|
||||
|
||||
@@ -104,16 +107,13 @@ func (v *{{.ViewName}}{{.TypeParamNames}}) UnmarshalJSONFrom(dec *jsontext.Decod
|
||||
{{define "valuePointerField"}}func (v {{.ViewName}}{{.TypeParamNames}}) {{.FieldName}}() views.ValuePointer[{{.FieldType}}] { return views.ValuePointerOf(v.ж.{{.FieldName}}) }
|
||||
|
||||
{{end}}
|
||||
{{define "mapField"}}
|
||||
func(v {{.ViewName}}{{.TypeParamNames}}) {{.FieldName}}() views.Map[{{.MapKeyType}},{{.MapValueType}}] { return views.MapOf(v.ж.{{.FieldName}})}
|
||||
{{define "mapField"}}func(v {{.ViewName}}{{.TypeParamNames}}) {{.FieldName}}() views.Map[{{.MapKeyType}},{{.MapValueType}}] { return views.MapOf(v.ж.{{.FieldName}})}
|
||||
{{end}}
|
||||
{{define "mapFnField"}}
|
||||
func(v {{.ViewName}}{{.TypeParamNames}}) {{.FieldName}}() views.MapFn[{{.MapKeyType}},{{.MapValueType}},{{.MapValueView}}] { return views.MapFnOf(v.ж.{{.FieldName}}, func (t {{.MapValueType}}) {{.MapValueView}} {
|
||||
{{define "mapFnField"}}func(v {{.ViewName}}{{.TypeParamNames}}) {{.FieldName}}() views.MapFn[{{.MapKeyType}},{{.MapValueType}},{{.MapValueView}}] { return views.MapFnOf(v.ж.{{.FieldName}}, func (t {{.MapValueType}}) {{.MapValueView}} {
|
||||
return {{.MapFn}}
|
||||
})}
|
||||
{{end}}
|
||||
{{define "mapSliceField"}}
|
||||
func(v {{.ViewName}}{{.TypeParamNames}}) {{.FieldName}}() views.MapSlice[{{.MapKeyType}},{{.MapValueType}}] { return views.MapSliceOf(v.ж.{{.FieldName}}) }
|
||||
{{define "mapSliceField"}}func(v {{.ViewName}}{{.TypeParamNames}}) {{.FieldName}}() views.MapSlice[{{.MapKeyType}},{{.MapValueType}}] { return views.MapSliceOf(v.ж.{{.FieldName}}) }
|
||||
{{end}}
|
||||
{{define "unsupportedField"}}func(v {{.ViewName}}{{.TypeParamNames}}) {{.FieldName}}() {{.FieldType}} {panic("unsupported")}
|
||||
{{end}}
|
||||
@@ -142,7 +142,81 @@ func requiresCloning(t types.Type) (shallow, deep bool, base types.Type) {
|
||||
return p, p, t
|
||||
}
|
||||
|
||||
func genView(buf *bytes.Buffer, it *codegen.ImportTracker, typ *types.Named, _ *types.Package) {
|
||||
type fieldNameKey struct {
|
||||
typeName string
|
||||
fieldName string
|
||||
}
|
||||
|
||||
// getFieldComments extracts field comments from the AST for a given struct type.
|
||||
func getFieldComments(syntax []*ast.File) map[fieldNameKey]string {
|
||||
if len(syntax) == 0 {
|
||||
return nil
|
||||
}
|
||||
var fieldComments map[fieldNameKey]string
|
||||
|
||||
// Search through all AST files in the package
|
||||
for _, file := range syntax {
|
||||
// Look for the type declaration
|
||||
for _, decl := range file.Decls {
|
||||
genDecl, ok := decl.(*ast.GenDecl)
|
||||
if !ok || genDecl.Tok != token.TYPE {
|
||||
continue
|
||||
}
|
||||
|
||||
for _, spec := range genDecl.Specs {
|
||||
typeSpec, ok := spec.(*ast.TypeSpec)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
typeName := typeSpec.Name.Name
|
||||
|
||||
// Check if it's a struct type
|
||||
structType, ok := typeSpec.Type.(*ast.StructType)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
// Extract field comments
|
||||
for _, field := range structType.Fields.List {
|
||||
if len(field.Names) == 0 {
|
||||
// Anonymous field or no names
|
||||
continue
|
||||
}
|
||||
|
||||
// Get the field name
|
||||
fieldName := field.Names[0].Name
|
||||
key := fieldNameKey{typeName, fieldName}
|
||||
|
||||
// Get the comment
|
||||
var comment string
|
||||
if field.Doc != nil && field.Doc.Text() != "" {
|
||||
// Format the comment for Go code generation
|
||||
comment = strings.TrimSpace(field.Doc.Text())
|
||||
// Convert multi-line comments to proper Go comment format
|
||||
var sb strings.Builder
|
||||
for line := range strings.Lines(comment) {
|
||||
sb.WriteString("// ")
|
||||
sb.WriteString(line)
|
||||
}
|
||||
if sb.Len() > 0 {
|
||||
comment = sb.String()
|
||||
}
|
||||
} else if field.Comment != nil && field.Comment.Text() != "" {
|
||||
// Handle inline comments
|
||||
comment = "// " + strings.TrimSpace(field.Comment.Text())
|
||||
}
|
||||
if comment != "" {
|
||||
mak.Set(&fieldComments, key, comment)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return fieldComments
|
||||
}
|
||||
|
||||
func genView(buf *bytes.Buffer, it *codegen.ImportTracker, typ *types.Named, fieldComments map[fieldNameKey]string) {
|
||||
t, ok := typ.Underlying().(*types.Struct)
|
||||
if !ok || codegen.IsViewType(t) {
|
||||
return
|
||||
@@ -182,6 +256,15 @@ func genView(buf *bytes.Buffer, it *codegen.ImportTracker, typ *types.Named, _ *
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
writeTemplateWithComment := func(name, fieldName string) {
|
||||
// Write the field comment if it exists
|
||||
key := fieldNameKey{args.StructName, fieldName}
|
||||
if comment, ok := fieldComments[key]; ok && comment != "" {
|
||||
fmt.Fprintln(buf, comment)
|
||||
}
|
||||
writeTemplate(name)
|
||||
}
|
||||
|
||||
writeTemplate("common")
|
||||
for i := range t.NumFields() {
|
||||
f := t.Field(i)
|
||||
@@ -196,7 +279,7 @@ func genView(buf *bytes.Buffer, it *codegen.ImportTracker, typ *types.Named, _ *
|
||||
}
|
||||
if !codegen.ContainsPointers(fieldType) || codegen.IsViewType(fieldType) || codegen.HasNoClone(t.Tag(i)) {
|
||||
args.FieldType = it.QualifiedName(fieldType)
|
||||
writeTemplate("valueField")
|
||||
writeTemplateWithComment("valueField", fname)
|
||||
continue
|
||||
}
|
||||
switch underlying := fieldType.Underlying().(type) {
|
||||
@@ -207,7 +290,7 @@ func genView(buf *bytes.Buffer, it *codegen.ImportTracker, typ *types.Named, _ *
|
||||
case "byte":
|
||||
args.FieldType = it.QualifiedName(fieldType)
|
||||
it.Import("", "tailscale.com/types/views")
|
||||
writeTemplate("byteSliceField")
|
||||
writeTemplateWithComment("byteSliceField", fname)
|
||||
default:
|
||||
args.FieldType = it.QualifiedName(elem)
|
||||
it.Import("", "tailscale.com/types/views")
|
||||
@@ -217,35 +300,35 @@ func genView(buf *bytes.Buffer, it *codegen.ImportTracker, typ *types.Named, _ *
|
||||
case *types.Pointer:
|
||||
if _, isIface := base.Underlying().(*types.Interface); !isIface {
|
||||
args.FieldViewName = appendNameSuffix(it.QualifiedName(base), "View")
|
||||
writeTemplate("viewSliceField")
|
||||
writeTemplateWithComment("viewSliceField", fname)
|
||||
} else {
|
||||
writeTemplate("unsupportedField")
|
||||
writeTemplateWithComment("unsupportedField", fname)
|
||||
}
|
||||
continue
|
||||
case *types.Interface:
|
||||
if viewType := viewTypeForValueType(elem); viewType != nil {
|
||||
args.FieldViewName = it.QualifiedName(viewType)
|
||||
writeTemplate("viewSliceField")
|
||||
writeTemplateWithComment("viewSliceField", fname)
|
||||
continue
|
||||
}
|
||||
}
|
||||
writeTemplate("unsupportedField")
|
||||
writeTemplateWithComment("unsupportedField", fname)
|
||||
continue
|
||||
} else if shallow {
|
||||
switch base.Underlying().(type) {
|
||||
case *types.Basic, *types.Interface:
|
||||
writeTemplate("unsupportedField")
|
||||
writeTemplateWithComment("unsupportedField", fname)
|
||||
default:
|
||||
if _, isIface := base.Underlying().(*types.Interface); !isIface {
|
||||
args.FieldViewName = appendNameSuffix(it.QualifiedName(base), "View")
|
||||
writeTemplate("viewSliceField")
|
||||
writeTemplateWithComment("viewSliceField", fname)
|
||||
} else {
|
||||
writeTemplate("unsupportedField")
|
||||
writeTemplateWithComment("unsupportedField", fname)
|
||||
}
|
||||
}
|
||||
continue
|
||||
}
|
||||
writeTemplate("sliceField")
|
||||
writeTemplateWithComment("sliceField", fname)
|
||||
}
|
||||
continue
|
||||
case *types.Struct:
|
||||
@@ -254,26 +337,26 @@ func genView(buf *bytes.Buffer, it *codegen.ImportTracker, typ *types.Named, _ *
|
||||
if codegen.ContainsPointers(strucT) {
|
||||
if viewType := viewTypeForValueType(fieldType); viewType != nil {
|
||||
args.FieldViewName = it.QualifiedName(viewType)
|
||||
writeTemplate("viewField")
|
||||
writeTemplateWithComment("viewField", fname)
|
||||
continue
|
||||
}
|
||||
if viewType, makeViewFn := viewTypeForContainerType(fieldType); viewType != nil {
|
||||
args.FieldViewName = it.QualifiedName(viewType)
|
||||
args.MakeViewFnName = it.PackagePrefix(makeViewFn.Pkg()) + makeViewFn.Name()
|
||||
writeTemplate("makeViewField")
|
||||
writeTemplateWithComment("makeViewField", fname)
|
||||
continue
|
||||
}
|
||||
writeTemplate("unsupportedField")
|
||||
writeTemplateWithComment("unsupportedField", fname)
|
||||
continue
|
||||
}
|
||||
writeTemplate("valueField")
|
||||
writeTemplateWithComment("valueField", fname)
|
||||
continue
|
||||
case *types.Map:
|
||||
m := underlying
|
||||
args.FieldType = it.QualifiedName(fieldType)
|
||||
shallow, deep, key := requiresCloning(m.Key())
|
||||
if shallow || deep {
|
||||
writeTemplate("unsupportedField")
|
||||
writeTemplateWithComment("unsupportedField", fname)
|
||||
continue
|
||||
}
|
||||
it.Import("", "tailscale.com/types/views")
|
||||
@@ -358,7 +441,7 @@ func genView(buf *bytes.Buffer, it *codegen.ImportTracker, typ *types.Named, _ *
|
||||
default:
|
||||
template = "unsupportedField"
|
||||
}
|
||||
writeTemplate(template)
|
||||
writeTemplateWithComment(template, fname)
|
||||
continue
|
||||
case *types.Pointer:
|
||||
ptr := underlying
|
||||
@@ -368,9 +451,9 @@ func genView(buf *bytes.Buffer, it *codegen.ImportTracker, typ *types.Named, _ *
|
||||
if _, isIface := base.Underlying().(*types.Interface); !isIface {
|
||||
args.FieldType = it.QualifiedName(base)
|
||||
args.FieldViewName = appendNameSuffix(args.FieldType, "View")
|
||||
writeTemplate("viewField")
|
||||
writeTemplateWithComment("viewField", fname)
|
||||
} else {
|
||||
writeTemplate("unsupportedField")
|
||||
writeTemplateWithComment("unsupportedField", fname)
|
||||
}
|
||||
continue
|
||||
}
|
||||
@@ -379,7 +462,7 @@ func genView(buf *bytes.Buffer, it *codegen.ImportTracker, typ *types.Named, _ *
|
||||
if viewType := viewTypeForValueType(base); viewType != nil {
|
||||
args.FieldType = it.QualifiedName(base)
|
||||
args.FieldViewName = it.QualifiedName(viewType)
|
||||
writeTemplate("viewField")
|
||||
writeTemplateWithComment("viewField", fname)
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -389,7 +472,7 @@ func genView(buf *bytes.Buffer, it *codegen.ImportTracker, typ *types.Named, _ *
|
||||
baseTypeName := it.QualifiedName(base)
|
||||
args.FieldType = baseTypeName
|
||||
args.FieldViewName = appendNameSuffix(args.FieldType, "View")
|
||||
writeTemplate("viewField")
|
||||
writeTemplateWithComment("viewField", fname)
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -397,18 +480,18 @@ func genView(buf *bytes.Buffer, it *codegen.ImportTracker, typ *types.Named, _ *
|
||||
// and will not have a generated view type, use views.ValuePointer[T] as the field's view type.
|
||||
// Its Get/GetOk methods return stack-allocated shallow copies of the field's value.
|
||||
args.FieldType = it.QualifiedName(base)
|
||||
writeTemplate("valuePointerField")
|
||||
writeTemplateWithComment("valuePointerField", fname)
|
||||
continue
|
||||
case *types.Interface:
|
||||
// If fieldType is an interface with a "View() {ViewType}" method, it can be used to clone the field.
|
||||
// This includes scenarios where fieldType is a constrained type parameter.
|
||||
if viewType := viewTypeForValueType(underlying); viewType != nil {
|
||||
args.FieldViewName = it.QualifiedName(viewType)
|
||||
writeTemplate("viewField")
|
||||
writeTemplateWithComment("viewField", fname)
|
||||
continue
|
||||
}
|
||||
}
|
||||
writeTemplate("unsupportedField")
|
||||
writeTemplateWithComment("unsupportedField", fname)
|
||||
}
|
||||
for i := range typ.NumMethods() {
|
||||
f := typ.Method(i)
|
||||
@@ -627,6 +710,7 @@ func main() {
|
||||
log.Fatal(err)
|
||||
}
|
||||
it := codegen.NewImportTracker(pkg.Types)
|
||||
fieldComments := getFieldComments(pkg.Syntax)
|
||||
|
||||
cloneOnlyType := map[string]bool{}
|
||||
for _, t := range strings.Split(*flagCloneOnlyTypes, ",") {
|
||||
@@ -654,7 +738,7 @@ func main() {
|
||||
if !hasClone {
|
||||
runCloner = true
|
||||
}
|
||||
genView(buf, it, typ, pkg.Types)
|
||||
genView(buf, it, typ, fieldComments)
|
||||
}
|
||||
out := pkg.Name + "_view"
|
||||
if *flagBuildTags == "test" {
|
||||
|
||||
@@ -53,6 +53,7 @@ func TestViewerImports(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
var fieldComments map[fieldNameKey]string // don't need it for this test.
|
||||
|
||||
var output bytes.Buffer
|
||||
tracker := codegen.NewImportTracker(pkg)
|
||||
@@ -65,7 +66,7 @@ func TestViewerImports(t *testing.T) {
|
||||
if !ok {
|
||||
t.Fatalf("%q is not a named type", tt.typeNames[i])
|
||||
}
|
||||
genView(&output, tracker, namedType, pkg)
|
||||
genView(&output, tracker, namedType, fieldComments)
|
||||
}
|
||||
|
||||
for _, pkg := range tt.wantImports {
|
||||
|
||||
Reference in New Issue
Block a user