Merge remote-tracking branch 'aosp/upstream'

* aosp/upstream:
  Correct bug in generating anonymous nested props
  Add docs for nested and embedded structs

Test: m soong_docs
Change-Id: Ide95609b6c7d221037348768ade719992801e411
This commit is contained in:
Liz Kammer 2020-10-05 15:09:42 -07:00
commit 2aeb413fa1
4 changed files with 159 additions and 22 deletions

View file

@ -5,6 +5,7 @@ import (
"html/template" "html/template"
"reflect" "reflect"
"sort" "sort"
"strings"
"github.com/google/blueprint/proptools" "github.com/google/blueprint/proptools"
) )
@ -58,6 +59,7 @@ type Property struct {
OtherTexts []template.HTML OtherTexts []template.HTML
Properties []Property Properties []Property
Default string Default string
Anonymous bool
} }
func AllPackages(pkgFiles map[string][]string, moduleTypeNameFactories map[string]reflect.Value, func AllPackages(pkgFiles map[string][]string, moduleTypeNameFactories map[string]reflect.Value,
@ -75,6 +77,7 @@ func AllPackages(pkgFiles map[string][]string, moduleTypeNameFactories map[strin
return nil, err return nil, err
} }
// Some pruning work // Some pruning work
removeAnonymousProperties(mtInfo)
removeEmptyPropertyStructs(mtInfo) removeEmptyPropertyStructs(mtInfo)
collapseDuplicatePropertyStructs(mtInfo) collapseDuplicatePropertyStructs(mtInfo)
collapseNestedPropertyStructs(mtInfo) collapseNestedPropertyStructs(mtInfo)
@ -128,7 +131,9 @@ func assembleModuleTypeInfo(r *Reader, name string, factory reflect.Value,
} }
ps.ExcludeByTag("blueprint", "mutated") ps.ExcludeByTag("blueprint", "mutated")
for nestedName, nestedValue := range nestedPropertyStructs(v) { for _, nestedProperty := range nestedPropertyStructs(v) {
nestedName := nestedProperty.nestPoint
nestedValue := nestedProperty.value
nestedType := nestedValue.Type() nestedType := nestedValue.Type()
// Ignore property structs with unexported or unnamed types // Ignore property structs with unexported or unnamed types
@ -140,12 +145,28 @@ func assembleModuleTypeInfo(r *Reader, name string, factory reflect.Value,
return nil, err return nil, err
} }
nested.ExcludeByTag("blueprint", "mutated") nested.ExcludeByTag("blueprint", "mutated")
nestPoint := ps.GetByName(nestedName) if nestedName == "" {
if nestPoint == nil { ps.Nest(nested)
return nil, fmt.Errorf("nesting point %q not found", nestedName) } else {
nestPoint := ps.GetByName(nestedName)
if nestPoint == nil {
return nil, fmt.Errorf("nesting point %q not found", nestedName)
}
nestPoint.Nest(nested)
} }
nestPoint.Nest(nested) if nestedProperty.anonymous {
if nestedName != "" {
nestedName += "."
}
nestedName += proptools.PropertyNameForField(nested.Name)
nestedProp := ps.GetByName(nestedName)
// Anonymous properties may have already been omitted, no need to ensure they are filtered later
if nestedProp != nil {
// Set property to anonymous to allow future filtering
nestedProp.SetAnonymous()
}
}
} }
mt.PropertyStructs = append(mt.PropertyStructs, ps) mt.PropertyStructs = append(mt.PropertyStructs, ps)
} }
@ -153,10 +174,31 @@ func assembleModuleTypeInfo(r *Reader, name string, factory reflect.Value,
return mt, nil return mt, nil
} }
func nestedPropertyStructs(s reflect.Value) map[string]reflect.Value { type nestedProperty struct {
ret := make(map[string]reflect.Value) nestPoint string
value reflect.Value
anonymous bool
}
func nestedPropertyStructs(s reflect.Value) []nestedProperty {
ret := make([]nestedProperty, 0)
var walk func(structValue reflect.Value, prefix string) var walk func(structValue reflect.Value, prefix string)
walk = func(structValue reflect.Value, prefix string) { walk = func(structValue reflect.Value, prefix string) {
var nestStruct func(field reflect.StructField, value reflect.Value, fieldName string)
nestStruct = func(field reflect.StructField, value reflect.Value, fieldName string) {
nestPoint := prefix
if field.Anonymous {
nestPoint = strings.TrimSuffix(nestPoint, ".")
} else {
nestPoint = nestPoint + proptools.PropertyNameForField(fieldName)
}
ret = append(ret, nestedProperty{nestPoint: nestPoint, value: value, anonymous: field.Anonymous})
if nestPoint != "" {
nestPoint += "."
}
walk(value, nestPoint)
}
typ := structValue.Type() typ := structValue.Type()
for i := 0; i < structValue.NumField(); i++ { for i := 0; i < structValue.NumField(); i++ {
field := typ.Field(i) field := typ.Field(i)
@ -174,8 +216,9 @@ func nestedPropertyStructs(s reflect.Value) map[string]reflect.Value {
case reflect.Bool, reflect.String, reflect.Slice, reflect.Int, reflect.Uint: case reflect.Bool, reflect.String, reflect.Slice, reflect.Int, reflect.Uint:
// Nothing // Nothing
case reflect.Struct: case reflect.Struct:
walk(fieldValue, prefix+proptools.PropertyNameForField(field.Name)+".") nestStruct(field, fieldValue, field.Name)
case reflect.Ptr, reflect.Interface: case reflect.Ptr, reflect.Interface:
if !fieldValue.IsNil() { if !fieldValue.IsNil() {
// We leave the pointer intact and zero out the struct that's // We leave the pointer intact and zero out the struct that's
// pointed to. // pointed to.
@ -188,9 +231,7 @@ func nestedPropertyStructs(s reflect.Value) map[string]reflect.Value {
elem = elem.Elem() elem = elem.Elem()
} }
if elem.Kind() == reflect.Struct { if elem.Kind() == reflect.Struct {
nestPoint := prefix + proptools.PropertyNameForField(field.Name) nestStruct(field, elem, field.Name)
ret[nestPoint] = elem
walk(elem, nestPoint+".")
} }
} }
default: default:
@ -214,6 +255,27 @@ func removeEmptyPropertyStructs(mt *ModuleType) {
} }
} }
// Remove any property structs that are anonymous
func removeAnonymousProperties(mt *ModuleType) {
var removeAnonymousProps func(props []Property) []Property
removeAnonymousProps = func(props []Property) []Property {
newProps := make([]Property, 0, len(props))
for _, p := range props {
if p.Anonymous {
continue
}
if len(p.Properties) > 0 {
p.Properties = removeAnonymousProps(p.Properties)
}
newProps = append(newProps, p)
}
return newProps
}
for _, ps := range mt.PropertyStructs {
ps.Properties = removeAnonymousProps(ps.Properties)
}
}
// Squashes duplicates of the same property struct into single entries // Squashes duplicates of the same property struct into single entries
func collapseDuplicatePropertyStructs(mt *ModuleType) { func collapseDuplicatePropertyStructs(mt *ModuleType) {
var collapsed []*PropertyStruct var collapsed []*PropertyStruct

View file

@ -36,20 +36,23 @@ func TestNestedPropertyStructs(t *testing.T) {
// mutated shouldn't be found because it's a mutated property. // mutated shouldn't be found because it's a mutated property.
expected := []string{"child", "child.child"} expected := []string{"child", "child.child"}
if len(allStructs) != len(expected) { if len(allStructs) != len(expected) {
t.Errorf("expected %d structs, got %d, all entries: %q", t.Fatalf("expected %d structs, got %d, all entries: %v",
len(expected), len(allStructs), allStructs) len(expected), len(allStructs), allStructs)
} }
for _, e := range expected { got := []string{}
if _, ok := allStructs[e]; !ok { for _, s := range allStructs {
t.Errorf("missing entry %q, all entries: %q", e, allStructs) got = append(got, s.nestPoint)
} }
if !reflect.DeepEqual(got, expected) {
t.Errorf("Expected nested properties:\n\t %q,\n but got\n\t %q", expected, got)
} }
} }
func TestAllPackages(t *testing.T) { func TestAllPackages(t *testing.T) {
packages, err := AllPackages(pkgFiles, moduleTypeNameFactories, moduleTypeNamePropertyStructs) packages, err := AllPackages(pkgFiles, moduleTypeNameFactories, moduleTypeNamePropertyStructs)
if err != nil { if err != nil {
t.Errorf("expected nil error for AllPackages(%v, %v, %v), got %s", pkgFiles, moduleTypeNameFactories, moduleTypeNamePropertyStructs, err) t.Fatalf("expected nil error for AllPackages(%v, %v, %v), got %s", pkgFiles, moduleTypeNameFactories, moduleTypeNamePropertyStructs, err)
} }
if numPackages := len(packages); numPackages != 1 { if numPackages := len(packages); numPackages != 1 {
@ -58,24 +61,56 @@ func TestAllPackages(t *testing.T) {
pkg := packages[0] pkg := packages[0]
expectedProps := map[string][]string{
"bar": []string{
"a",
"nested",
"nested.c",
"nested_struct",
"nested_struct.e",
"struct_has_embed",
"struct_has_embed.nested_in_embedded",
"struct_has_embed.nested_in_embedded.e",
"struct_has_embed.f",
"nested_in_other_embedded",
"nested_in_other_embedded.g",
"h",
},
"foo": []string{
"a",
},
}
for _, m := range pkg.ModuleTypes { for _, m := range pkg.ModuleTypes {
foundProps := []string{}
for _, p := range m.PropertyStructs { for _, p := range m.PropertyStructs {
for _, err := range noMutatedProperties(p.Properties) { nestedProps, errs := findAllProperties("", p.Properties)
foundProps = append(foundProps, nestedProps...)
for _, err := range errs {
t.Errorf("%s", err) t.Errorf("%s", err)
} }
} }
if wanted, ok := expectedProps[m.Name]; ok {
if !reflect.DeepEqual(foundProps, wanted) {
t.Errorf("For %s, expected\n\t %q,\nbut got\n\t %q", m.Name, wanted, foundProps)
}
}
} }
} }
func noMutatedProperties(properties []Property) []error { func findAllProperties(prefix string, properties []Property) ([]string, []error) {
foundProps := []string{}
errs := []error{} errs := []error{}
for _, p := range properties { for _, p := range properties {
foundProps = append(foundProps, prefix+p.Name)
if hasTag(p.Tag, "blueprint", "mutated") { if hasTag(p.Tag, "blueprint", "mutated") {
err := fmt.Errorf("Property %s has `blueprint:\"mutated\" tag but should have been excluded.", p) err := fmt.Errorf("Property %s has `blueprint:\"mutated\" tag but should have been excluded.", p.Name)
errs = append(errs, err) errs = append(errs, err)
} }
errs = append(errs, noMutatedProperties(p.Properties)...) nestedProps, nestedErrs := findAllProperties(prefix+p.Name+".", p.Properties)
foundProps = append(foundProps, nestedProps...)
errs = append(errs, nestedErrs...)
} }
return errs return foundProps, errs
} }

View file

@ -142,6 +142,10 @@ func (ps *PropertyStruct) GetByName(name string) *Property {
return getByName(name, "", &ps.Properties) return getByName(name, "", &ps.Properties)
} }
func (ps *PropertyStruct) Nest(nested *PropertyStruct) {
ps.Properties = append(ps.Properties, nested.Properties...)
}
func getByName(name string, prefix string, props *[]Property) *Property { func getByName(name string, prefix string, props *[]Property) *Property {
for i := range *props { for i := range *props {
if prefix+(*props)[i].Name == name { if prefix+(*props)[i].Name == name {
@ -157,6 +161,10 @@ func (p *Property) Nest(nested *PropertyStruct) {
p.Properties = append(p.Properties, nested.Properties...) p.Properties = append(p.Properties, nested.Properties...)
} }
func (p *Property) SetAnonymous() {
p.Anonymous = true
}
func newPropertyStruct(t *doc.Type) (*PropertyStruct, error) { func newPropertyStruct(t *doc.Type) (*PropertyStruct, error) {
typeSpec := t.Decl.Specs[0].(*ast.TypeSpec) typeSpec := t.Decl.Specs[0].(*ast.TypeSpec)
ps := PropertyStruct{ ps := PropertyStruct{

View file

@ -36,6 +36,32 @@ func barFactory() (blueprint.Module, []interface{}) {
return nil, []interface{}{&complexProps{}} return nil, []interface{}{&complexProps{}}
} }
type structToNest struct {
E string
}
type StructToEmbed struct {
Nested_in_embedded structToNest
// F string
F string
}
type otherStructToNest struct {
G string
}
type OtherStructToEmbed struct {
Nested_in_other_embedded otherStructToNest
// F string
H string
}
type StructWithEmbedded struct {
StructToEmbed
}
// for bpdoc_test.go // for bpdoc_test.go
type complexProps struct { type complexProps struct {
A string A string
@ -45,6 +71,12 @@ type complexProps struct {
C string C string
D_mutated string `blueprint:"mutated"` D_mutated string `blueprint:"mutated"`
} }
Nested_struct structToNest
Struct_has_embed StructWithEmbedded
OtherStructToEmbed
} }
// props docs. // props docs.