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 }