cuelang.org/go@v0.13.0/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 fdocs, _ := internal.FileComments(f) 60 docs = append(docs, fdocs...) 61 } 62 63 return true 64 }) 65 66 // Collect docs from parent scopes in collapsed fields. 67 for p := v.Parent; p != nil; p = p.Parent { 68 69 newFields := []*ast.Field{} 70 71 p.VisitLeafConjuncts(func(x adt.Conjunct) bool { 72 f, ok := x.Source().(*ast.Field) 73 if !ok || !hasShorthandValue(f) { 74 return true 75 } 76 77 nested := nestedField(f) 78 for _, child := range fields { 79 if nested == child { 80 newFields = append(newFields, f) 81 for _, cg := range f.Comments() { 82 if !containsDoc(docs, cg) && cg.Doc { 83 docs = append(docs, cg) 84 } 85 } 86 } 87 } 88 return true 89 }) 90 91 fields = newFields 92 } 93 return docs 94 } 95 96 // hasShorthandValue reports whether this field has a struct value that will 97 // be rendered as a shorthand, for instance: 98 // 99 // f: g: 2 100 func hasShorthandValue(f *ast.Field) bool { 101 if f = nestedField(f); f == nil { 102 return false 103 } 104 105 // Not a regular field, but shorthand field. 106 // TODO: Should we return here? For now mimic old implementation. 107 if _, _, err := ast.LabelName(f.Label); err != nil { 108 return false 109 } 110 111 return true 112 } 113 114 // nestedField returns the child field of a field shorthand. 115 func nestedField(f *ast.Field) *ast.Field { 116 s, _ := f.Value.(*ast.StructLit) 117 if s == nil || 118 len(s.Elts) != 1 || 119 s.Lbrace != token.NoPos || 120 s.Rbrace != token.NoPos { 121 return nil 122 } 123 124 f, _ = s.Elts[0].(*ast.Field) 125 return f 126 } 127 128 func containsDoc(a []*ast.CommentGroup, cg *ast.CommentGroup) bool { 129 for _, c := range a { 130 if c == cg { 131 return true 132 } 133 } 134 135 for _, c := range a { 136 if c.Text() == cg.Text() { 137 return true 138 } 139 } 140 141 return false 142 } 143 144 func ExtractFieldAttrs(v *adt.Vertex) (attrs []*ast.Attribute) { 145 v.VisitLeafConjuncts(func(x adt.Conjunct) bool { 146 attrs = extractFieldAttrs(attrs, x.Field()) 147 return true 148 }) 149 return attrs 150 } 151 152 // extractFieldAttrs extracts the fields from n and appends unique entries to 153 // attrs. 154 // 155 // The value of n should be obtained from the Conjunct.Field method if the 156 // source for n is a Conjunct so that Comprehensions are properly unwrapped. 157 func extractFieldAttrs(attrs []*ast.Attribute, n adt.Node) []*ast.Attribute { 158 if f, ok := n.Source().(*ast.Field); ok { 159 for _, a := range f.Attrs { 160 if !containsAttr(attrs, a) { 161 attrs = append(attrs, a) 162 } 163 } 164 } 165 return attrs 166 } 167 168 func ExtractDeclAttrs(v *adt.Vertex) (attrs []*ast.Attribute) { 169 for _, st := range v.Structs { 170 if src := st.StructLit; src != nil { 171 attrs = extractDeclAttrs(attrs, src.Src) 172 } 173 } 174 return attrs 175 } 176 177 func extractDeclAttrs(attrs []*ast.Attribute, n ast.Node) []*ast.Attribute { 178 switch x := n.(type) { 179 case nil: 180 case *ast.File: 181 attrs = appendDeclAttrs(attrs, x.Decls[len(x.Preamble()):]) 182 case *ast.StructLit: 183 attrs = appendDeclAttrs(attrs, x.Elts) 184 } 185 return attrs 186 } 187 188 func appendDeclAttrs(a []*ast.Attribute, decls []ast.Decl) []*ast.Attribute { 189 for _, d := range decls { 190 if attr, ok := d.(*ast.Attribute); ok && !containsAttr(a, attr) { 191 a = append(a, attr) 192 } 193 } 194 return a 195 } 196 197 func containsAttr(a []*ast.Attribute, x *ast.Attribute) bool { 198 for _, e := range a { 199 if e.Text == x.Text { 200 return true 201 } 202 } 203 return false 204 }