...

Source file src/cuelang.org/go/internal/core/export/extract.go

Documentation: cuelang.org/go/internal/core/export

     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, v.Conjuncts)
    33  }
    34  
    35  func extractDocs(v *adt.Vertex, a []adt.Conjunct) (docs []*ast.CommentGroup) {
    36  	fields := []*ast.Field{}
    37  
    38  	// Collect docs directly related to this Vertex.
    39  	for _, x := range a {
    40  		// TODO: Is this still being used?
    41  		if v, ok := x.Elem().(*adt.Vertex); ok {
    42  			docs = append(docs, extractDocs(v, v.Conjuncts)...)
    43  			continue
    44  		}
    45  
    46  		switch f := x.Field().Source().(type) {
    47  		case *ast.Field:
    48  			if hasShorthandValue(f) {
    49  				continue
    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  
    65  	if v == nil {
    66  		return docs
    67  	}
    68  
    69  	// Collect docs from parent scopes in collapsed fields.
    70  	for p := v.Parent; p != nil; p = p.Parent {
    71  
    72  		newFields := []*ast.Field{}
    73  
    74  		for _, x := range p.Conjuncts {
    75  			f, ok := x.Source().(*ast.Field)
    76  			if !ok || !hasShorthandValue(f) {
    77  				continue
    78  			}
    79  
    80  			nested := nestedField(f)
    81  			for _, child := range fields {
    82  				if nested == child {
    83  					newFields = append(newFields, f)
    84  					for _, cg := range f.Comments() {
    85  						if !containsDoc(docs, cg) && cg.Doc {
    86  							docs = append(docs, cg)
    87  						}
    88  					}
    89  				}
    90  			}
    91  		}
    92  
    93  		fields = newFields
    94  	}
    95  	return docs
    96  }
    97  
    98  // hasShorthandValue reports whether this field has a struct value that will
    99  // be rendered as a shorthand, for instance:
   100  //
   101  //	f: g: 2
   102  func hasShorthandValue(f *ast.Field) bool {
   103  	if f = nestedField(f); f == nil {
   104  		return false
   105  	}
   106  
   107  	// Not a regular field, but shorthand field.
   108  	// TODO: Should we return here? For now mimic old implementation.
   109  	if _, _, err := ast.LabelName(f.Label); err != nil {
   110  		return false
   111  	}
   112  
   113  	return true
   114  }
   115  
   116  // nestedField returns the child field of a field shorthand.
   117  func nestedField(f *ast.Field) *ast.Field {
   118  	s, _ := f.Value.(*ast.StructLit)
   119  	if s == nil ||
   120  		len(s.Elts) != 1 ||
   121  		s.Lbrace != token.NoPos ||
   122  		s.Rbrace != token.NoPos {
   123  		return nil
   124  	}
   125  
   126  	f, _ = s.Elts[0].(*ast.Field)
   127  	return f
   128  }
   129  
   130  func containsDoc(a []*ast.CommentGroup, cg *ast.CommentGroup) bool {
   131  	for _, c := range a {
   132  		if c == cg {
   133  			return true
   134  		}
   135  	}
   136  
   137  	for _, c := range a {
   138  		if c.Text() == cg.Text() {
   139  			return true
   140  		}
   141  	}
   142  
   143  	return false
   144  }
   145  
   146  func ExtractFieldAttrs(v *adt.Vertex) (attrs []*ast.Attribute) {
   147  	for _, x := range v.Conjuncts {
   148  		attrs = extractFieldAttrs(attrs, x.Field())
   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  }
   207  

View as plain text