cuelang.org/go@v0.10.1/internal/core/export/extract.go (about)

     1  // Copyright 2020 CUE Authors
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package export
    16  
    17  import (
    18  	"cuelang.org/go/cue/ast"
    19  	"cuelang.org/go/cue/token"
    20  	"cuelang.org/go/internal"
    21  	"cuelang.org/go/internal/core/adt"
    22  )
    23  
    24  // ExtractDoc collects documentation strings for a field.
    25  //
    26  // Comments are attached to a field with a field shorthand belong to the
    27  // child node. So in the following the comment is attached to field bar.
    28  //
    29  //	// comment
    30  //	foo: bar: 2
    31  func ExtractDoc(v *adt.Vertex) (docs []*ast.CommentGroup) {
    32  	return extractDocs(v)
    33  }
    34  
    35  func extractDocs(v *adt.Vertex) (docs []*ast.CommentGroup) {
    36  	fields := []*ast.Field{}
    37  
    38  	// Collect docs directly related to this Vertex.
    39  	v.VisitLeafConjuncts(func(x adt.Conjunct) bool {
    40  		// TODO: Is this still being used?
    41  		if v, ok := x.Elem().(*adt.Vertex); ok {
    42  			docs = append(docs, extractDocs(v)...)
    43  			return true
    44  		}
    45  
    46  		switch f := x.Field().Source().(type) {
    47  		case *ast.Field:
    48  			if hasShorthandValue(f) {
    49  				return true
    50  			}
    51  			fields = append(fields, f)
    52  			for _, cg := range f.Comments() {
    53  				if !containsDoc(docs, cg) && cg.Doc {
    54  					docs = append(docs, cg)
    55  				}
    56  			}
    57  
    58  		case *ast.File:
    59  			if c := internal.FileComment(f); c != nil {
    60  				docs = append(docs, c)
    61  			}
    62  		}
    63  
    64  		return true
    65  	})
    66  
    67  	// Collect docs from parent scopes in collapsed fields.
    68  	for p := v.Parent; p != nil; p = p.Parent {
    69  
    70  		newFields := []*ast.Field{}
    71  
    72  		p.VisitLeafConjuncts(func(x adt.Conjunct) bool {
    73  			f, ok := x.Source().(*ast.Field)
    74  			if !ok || !hasShorthandValue(f) {
    75  				return true
    76  			}
    77  
    78  			nested := nestedField(f)
    79  			for _, child := range fields {
    80  				if nested == child {
    81  					newFields = append(newFields, f)
    82  					for _, cg := range f.Comments() {
    83  						if !containsDoc(docs, cg) && cg.Doc {
    84  							docs = append(docs, cg)
    85  						}
    86  					}
    87  				}
    88  			}
    89  			return true
    90  		})
    91  
    92  		fields = newFields
    93  	}
    94  	return docs
    95  }
    96  
    97  // hasShorthandValue reports whether this field has a struct value that will
    98  // be rendered as a shorthand, for instance:
    99  //
   100  //	f: g: 2
   101  func hasShorthandValue(f *ast.Field) bool {
   102  	if f = nestedField(f); f == nil {
   103  		return false
   104  	}
   105  
   106  	// Not a regular field, but shorthand field.
   107  	// TODO: Should we return here? For now mimic old implementation.
   108  	if _, _, err := ast.LabelName(f.Label); err != nil {
   109  		return false
   110  	}
   111  
   112  	return true
   113  }
   114  
   115  // nestedField returns the child field of a field shorthand.
   116  func nestedField(f *ast.Field) *ast.Field {
   117  	s, _ := f.Value.(*ast.StructLit)
   118  	if s == nil ||
   119  		len(s.Elts) != 1 ||
   120  		s.Lbrace != token.NoPos ||
   121  		s.Rbrace != token.NoPos {
   122  		return nil
   123  	}
   124  
   125  	f, _ = s.Elts[0].(*ast.Field)
   126  	return f
   127  }
   128  
   129  func containsDoc(a []*ast.CommentGroup, cg *ast.CommentGroup) bool {
   130  	for _, c := range a {
   131  		if c == cg {
   132  			return true
   133  		}
   134  	}
   135  
   136  	for _, c := range a {
   137  		if c.Text() == cg.Text() {
   138  			return true
   139  		}
   140  	}
   141  
   142  	return false
   143  }
   144  
   145  func ExtractFieldAttrs(v *adt.Vertex) (attrs []*ast.Attribute) {
   146  	v.VisitLeafConjuncts(func(x adt.Conjunct) bool {
   147  		attrs = extractFieldAttrs(attrs, x.Field())
   148  		return true
   149  	})
   150  	return attrs
   151  }
   152  
   153  // extractFieldAttrs extracts the fields from n and appends unique entries to
   154  // attrs.
   155  //
   156  // The value of n should be obtained from the Conjunct.Field method if the
   157  // source for n is a Conjunct so that Comprehensions are properly unwrapped.
   158  func extractFieldAttrs(attrs []*ast.Attribute, n adt.Node) []*ast.Attribute {
   159  	if f, ok := n.Source().(*ast.Field); ok {
   160  		for _, a := range f.Attrs {
   161  			if !containsAttr(attrs, a) {
   162  				attrs = append(attrs, a)
   163  			}
   164  		}
   165  	}
   166  	return attrs
   167  }
   168  
   169  func ExtractDeclAttrs(v *adt.Vertex) (attrs []*ast.Attribute) {
   170  	for _, st := range v.Structs {
   171  		if src := st.StructLit; src != nil {
   172  			attrs = extractDeclAttrs(attrs, src.Src)
   173  		}
   174  	}
   175  	return attrs
   176  }
   177  
   178  func extractDeclAttrs(attrs []*ast.Attribute, n ast.Node) []*ast.Attribute {
   179  	switch x := n.(type) {
   180  	case nil:
   181  	case *ast.File:
   182  		info := internal.GetPackageInfo(x)
   183  		attrs = appendDeclAttrs(attrs, x.Decls[info.Index:])
   184  	case *ast.StructLit:
   185  		attrs = appendDeclAttrs(attrs, x.Elts)
   186  	}
   187  	return attrs
   188  }
   189  
   190  func appendDeclAttrs(a []*ast.Attribute, decls []ast.Decl) []*ast.Attribute {
   191  	for _, d := range decls {
   192  		if attr, ok := d.(*ast.Attribute); ok && !containsAttr(a, attr) {
   193  			a = append(a, attr)
   194  		}
   195  	}
   196  	return a
   197  }
   198  
   199  func containsAttr(a []*ast.Attribute, x *ast.Attribute) bool {
   200  	for _, e := range a {
   201  		if e.Text == x.Text {
   202  			return true
   203  		}
   204  	}
   205  	return false
   206  }