github.com/solo-io/cue@v0.4.7/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 "github.com/solo-io/cue/cue/ast" 19 "github.com/solo-io/cue/cue/token" 20 "github.com/solo-io/cue/internal" 21 "github.com/solo-io/cue/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 // 32 func ExtractDoc(v *adt.Vertex) (docs []*ast.CommentGroup) { 33 return extractDocs(v, v.Conjuncts) 34 } 35 36 func extractDocs(v *adt.Vertex, a []adt.Conjunct) (docs []*ast.CommentGroup) { 37 fields := []*ast.Field{} 38 39 // Collect docs directly related to this Vertex. 40 for _, x := range a { 41 if v, ok := x.Expr().(*adt.Vertex); ok { 42 docs = append(docs, extractDocs(v, v.Conjuncts)...) 43 continue 44 } 45 46 switch f := x.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 // 103 func hasShorthandValue(f *ast.Field) bool { 104 if f = nestedField(f); f == nil { 105 return false 106 } 107 108 // Not a regular field, but shorthand field. 109 // TODO: Should we return here? For now mimic old implementation. 110 if _, _, err := ast.LabelName(f.Label); err != nil { 111 return false 112 } 113 114 return true 115 } 116 117 // nestedField returns the child field of a field shorthand. 118 func nestedField(f *ast.Field) *ast.Field { 119 s, _ := f.Value.(*ast.StructLit) 120 if s == nil || 121 len(s.Elts) != 1 || 122 s.Lbrace != token.NoPos || 123 s.Rbrace != token.NoPos { 124 return nil 125 } 126 127 f, _ = s.Elts[0].(*ast.Field) 128 return f 129 } 130 131 func containsDoc(a []*ast.CommentGroup, cg *ast.CommentGroup) bool { 132 for _, c := range a { 133 if c == cg { 134 return true 135 } 136 } 137 138 for _, c := range a { 139 if c.Text() == cg.Text() { 140 return true 141 } 142 } 143 144 return false 145 } 146 147 func ExtractFieldAttrs(v *adt.Vertex) (attrs []*ast.Attribute) { 148 for _, x := range v.Conjuncts { 149 attrs = extractFieldAttrs(attrs, x) 150 } 151 return attrs 152 } 153 154 func extractFieldAttrs(attrs []*ast.Attribute, c adt.Conjunct) []*ast.Attribute { 155 if f, ok := c.Source().(*ast.Field); ok { 156 for _, a := range f.Attrs { 157 if !containsAttr(attrs, a) { 158 attrs = append(attrs, a) 159 } 160 } 161 } 162 return attrs 163 } 164 165 func ExtractDeclAttrs(v *adt.Vertex) (attrs []*ast.Attribute) { 166 for _, st := range v.Structs { 167 if src := st.StructLit; src != nil { 168 attrs = extractDeclAttrs(attrs, src.Src) 169 } 170 } 171 return attrs 172 } 173 174 func extractDeclAttrs(attrs []*ast.Attribute, n ast.Node) []*ast.Attribute { 175 switch x := n.(type) { 176 case nil: 177 case *ast.File: 178 info := internal.GetPackageInfo(x) 179 attrs = appendDeclAttrs(attrs, x.Decls[info.Index:]) 180 case *ast.StructLit: 181 attrs = appendDeclAttrs(attrs, x.Elts) 182 } 183 return attrs 184 } 185 186 func appendDeclAttrs(a []*ast.Attribute, decls []ast.Decl) []*ast.Attribute { 187 for _, d := range decls { 188 if attr, ok := d.(*ast.Attribute); ok && !containsAttr(a, attr) { 189 a = append(a, attr) 190 } 191 } 192 return a 193 } 194 195 func containsAttr(a []*ast.Attribute, x *ast.Attribute) bool { 196 for _, e := range a { 197 if e.Text == x.Text { 198 return true 199 } 200 } 201 return false 202 }