this repo has no description
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

internal/core/runtime: refactor ExtractAttrsByKind

The current logic in ExtractAttrsByKind is targetted specifically at the
use case of searching for a specific kind of attribute, but it's useful
to be able to walk all extern attributes in one pass without necessarily
knowing which one to look for.

We refactor ExtractAttrsByKind to be iterator-oriented instead, rename
it to ExternAttrsForFile, and change interpreter/embed.EmbeddedPaths to
use it directly. Technically that's an API-breaking change, but given
that its signature was defined in terms of an internal type
(internal.Attr), it wasn't possible for external callers to use it
anyway, so this should not break any users.

As part of this change, we move towards a situation where the top level
`@extern` attribute can define parameters that apply to all the
corresponding extern attributes in that file. This will be useful in the
up-coming plugin work.

Signed-off-by: Roger Peppe <rogpeppe@gmail.com>
Change-Id: I2bf84caa05dcdbc9f97fdc9296efa3d22b41928d
Reviewed-on: https://review.gerrithub.io/c/cue-lang/cue/+/1235295
Unity-Result: CUE porcuepine <cue.porcuepine@gmail.com>
Reviewed-by: Matthew Sackman <matthew@cue.works>
TryBot-Result: CUEcueckoo <cueckoo@cuelang.org>

+127 -77
+1
cmd/cue/cmd/integration/workspace/embedded_test.go
··· 384 384 URI: p.mapper.URI, 385 385 Range: protocol.Range{Start: pos}, 386 386 }) 387 + qt.Assert(t, qt.IsNotNil(got), qt.Commentf("%v(+%d)", p, i)) 387 388 qt.Assert(t, qt.Equals(got.Value, expectation), qt.Commentf("%v(+%d)", p, i)) 388 389 } 389 390 }
+41 -32
cue/interpreter/embed/embed.go
··· 166 166 // validateAttr performs logical validation of the attr. It does not 167 167 // perform any checks against a filesystem. 168 168 func validateAttr(a *internal.Attr) (file, glob, typ string, allowEmptyGlob bool, errs errors.Error) { 169 + if a.Err != nil { 170 + return "", "", "", false, a.Err 171 + } 172 + 169 173 pos := a.Pos 170 174 171 175 file, _, err := a.Lookup(0, "file") ··· 434 438 return v, nil 435 439 } 436 440 437 - // EmbeddedPaths validates the provided attributes as embed 438 - // attributes, returning a slice of [Embed] structs for attributes 441 + // EmbeddedPaths walks all the embed attributes in the given file, 442 + // returning a slice of [Embed] structs for attributes 439 443 // that were successfully validated, and errors for those which were 440 444 // not. The filepath should be the filepath of the file from which 441 445 // these attributes were extracted, and relative to whatever root is 442 446 // going to be used in calls to [Embed.Matches] and [Embed.FindAll]. 443 - func EmbeddedPaths(filepath string, attrsByNode map[ast.Node][]*internal.Attr) ([]*Embed, errors.Error) { 444 - if len(attrsByNode) == 0 { 447 + func EmbeddedPaths(filepath string, syntax *ast.File) ([]*Embed, errors.Error) { 448 + extAttrs, err := runtime.ExternAttrsForFile(syntax) 449 + if err != nil { 450 + return nil, err 451 + } 452 + if extAttrs.TopLevel[EmbedKind] == nil { 445 453 return nil, nil 446 454 } 447 455 var errs errors.Error 448 - embeds := make([]*Embed, 0, len(attrsByNode)) 449 - for node, attrs := range attrsByNode { 450 - for _, attr := range attrs { 451 - if attr.Err != nil { 452 - errs = errors.Append(errs, attr.Err) 453 - continue 454 - } 455 - file, glob, typ, allowEmptyGlob, err := validateAttr(attr) 456 - if err != nil { 457 - errs = errors.Append(errs, err) 458 - continue 459 - } 460 - embed := &Embed{ 461 - Node: node, 462 - Attribute: attr, 463 - FilePath: filepath, 464 - Type: typ, 456 + var embeds []*Embed 457 + for attr := range extAttrs.Body { 458 + if attr.Attr.Name != EmbedKind { 459 + continue 460 + } 461 + if err := attr.Attr.Err; err != nil { 462 + errs = errors.Append(errs, err) 463 + continue 464 + } 465 + file, glob, typ, allowEmptyGlob, err := validateAttr(attr.Attr) 466 + if err != nil { 467 + errs = errors.Append(errs, err) 468 + continue 469 + } 470 + embed := &Embed{ 471 + Node: attr.Parent, 472 + Attribute: attr.Attr, 473 + FilePath: filepath, 474 + Type: typ, 475 + } 476 + if file != "" { 477 + embed.interpreter = &embeddedFile{ 478 + filepath: file, 465 479 } 466 - if file != "" { 467 - embed.interpreter = &embeddedFile{ 468 - filepath: file, 469 - } 470 - embeds = append(embeds, embed) 471 - } else if glob != "" { 472 - embed.interpreter = &embeddedGlob{ 473 - glob: glob, 474 - allowEmptyGlob: allowEmptyGlob, 475 - } 476 - embeds = append(embeds, embed) 480 + embeds = append(embeds, embed) 481 + } else if glob != "" { 482 + embed.interpreter = &embeddedGlob{ 483 + glob: glob, 484 + allowEmptyGlob: allowEmptyGlob, 477 485 } 486 + embeds = append(embeds, embed) 478 487 } 479 488 } 480 489 return embeds, errs
+79 -38
internal/core/runtime/extern.go
··· 15 15 package runtime 16 16 17 17 import ( 18 + "iter" 19 + 18 20 "cuelang.org/go/cue/ast" 19 21 "cuelang.org/go/cue/build" 20 22 "cuelang.org/go/cue/errors" ··· 114 116 } 115 117 d.fileKinds[f.Pos().File()] = km 116 118 117 - for kind, pos := range kinds { 118 - if err := d.initCompiler(kind, pos); err != nil { 119 + for kind, attr := range kinds { 120 + if err := d.initCompiler(kind, attr.Pos); err != nil { 119 121 errs = errors.Append(errs, err) 120 122 } 121 123 } ··· 127 129 // declarations from the package directive onwards. It's an error if duplicate 128 130 // @extern attributes for the same kind are found. decls == nil signals that 129 131 // this file should be skipped. 130 - func findExternFileAttrs(f *ast.File) (kinds map[string]token.Pos, decls []ast.Decl, err errors.Error) { 132 + func findExternFileAttrs(f *ast.File) (kinds map[string]*internal.Attr, decls []ast.Decl, err errors.Error) { 131 133 var ( 132 134 hasPkg bool 133 135 p int ··· 166 168 } 167 169 168 170 if kinds == nil { 169 - kinds = map[string]token.Pos{} 171 + kinds = map[string]*internal.Attr{} 170 172 } 171 173 if _, ok := kinds[k]; ok { 172 174 err = errors.Append(err, errors.Newf(attr.Pos, 173 175 "duplicate @extern attribute for kind %q", k)) 174 176 continue 175 177 } 176 - kinds[k] = attr.Pos 178 + kinds[k] = attr 177 179 } 178 180 } 179 181 ··· 228 230 return nil 229 231 } 230 232 231 - // ExtractAttrsByKind finds all the attributes of the given kind in 232 - // the given AST, parsing their bodies into [internal.Attr]. 233 - func ExtractAttrsByKind(file *ast.File, kind string) (attrsByNode map[ast.Node][]*internal.Attr, errs errors.Error) { 233 + type ExternAttrs struct { 234 + // TopLevel holds all the extern attributes declared 235 + // before the package directive, e.g. @extern(embed) 236 + TopLevel map[string]*internal.Attr 237 + 238 + // Body holds a sequence of (node, attribute) pairs 239 + // corresponding to all the extern attributes in the body 240 + // of the file. 241 + Body iter.Seq[ExternAttr] 242 + } 243 + 244 + type ExternAttr struct { 245 + // TopLevel holds the top level @extern attribute for the attribute, for example @extern(embed). 246 + // This is the same as ExternAttrs.TopLevel[TopLevel.Name]. 247 + TopLevel *internal.Attr 248 + 249 + // Parent holds the parent AST node that contains the attribute. 250 + // It's either a *ast.Field, *ast.StructLit, or *ast.File. 251 + Parent ast.Node 252 + 253 + // Attr holds the extern attribute itself. 254 + Attr *internal.Attr 255 + } 256 + 257 + func ExternAttrsForFile(file *ast.File) (*ExternAttrs, errors.Error) { 234 258 kinds, decls, err := findExternFileAttrs(file) 235 - if err != nil || len(decls) == 0 { 259 + if err != nil { 236 260 return nil, err 237 261 } 238 - if _, ok := kinds[kind]; !ok { 239 - return nil, nil 240 - } 241 - 242 - nodeStack := []ast.Node{file} 262 + return &ExternAttrs{ 263 + TopLevel: kinds, 264 + Body: func(yield func(ExternAttr) bool) { 265 + if len(kinds) > 0 { 266 + walkExternFileAttrs(file, decls, kinds, yield) 267 + } 268 + }, 269 + }, nil 270 + } 243 271 272 + func walkExternFileAttrs(file *ast.File, decls []ast.Decl, kinds map[string]*internal.Attr, yield func(ExternAttr) bool) { 244 273 ast.Walk(&ast.File{Decls: decls}, func(n ast.Node) bool { 274 + var elts []ast.Decl 275 + parent := n 245 276 switch n := n.(type) { 246 277 case *ast.StructLit: 247 - nodeStack = append(nodeStack, n) 248 - 249 - case *ast.Field: 250 - nodeStack = append(nodeStack, n.Value) 251 - 252 - case *ast.Attribute: 253 - if n.Name() != kind { 254 - break 255 - } 256 - 257 - attrParsed := internal.ParseAttr(n) 258 - parent := nodeStack[len(nodeStack)-1] 259 - if attrsByNode == nil { 260 - attrsByNode = make(map[ast.Node][]*internal.Attr) 278 + elts = n.Elts 279 + case *ast.File: 280 + parent = file 281 + elts = n.Decls 282 + default: 283 + return true 284 + } 285 + for _, elt := range elts { 286 + switch elt := elt.(type) { 287 + case *ast.Attribute: 288 + if !yieldAttr(elt, parent, kinds, yield) { 289 + return false 290 + } 291 + case *ast.Field: 292 + for _, attr := range elt.Attrs { 293 + if !yieldAttr(attr, elt, kinds, yield) { 294 + return false 295 + } 296 + } 261 297 } 262 - attrsByNode[parent] = append(attrsByNode[parent], attrParsed) 263 - return false 264 298 } 299 + return true 300 + }, nil) 301 + } 265 302 303 + func yieldAttr(attr *ast.Attribute, parent ast.Node, kinds map[string]*internal.Attr, yield func(ExternAttr) bool) bool { 304 + toplevel := kinds[attr.Name()] 305 + if toplevel == nil { 266 306 return true 267 - 268 - }, func(n ast.Node) { 269 - switch n.(type) { 270 - case *ast.StructLit, *ast.Field: 271 - nodeStack = nodeStack[:len(nodeStack)-1] 272 - } 307 + } 308 + return yield(ExternAttr{ 309 + TopLevel: toplevel, 310 + Parent: parent, 311 + Attr: internal.ParseAttr(attr), 273 312 }) 274 - 275 - return attrsByNode, errs 276 313 } 277 314 278 315 func (d *externDecorator) decorateConjunct(e adt.Elem, scope *adt.Vertex) { ··· 368 405 } 369 406 return b 370 407 } 408 + 409 + func ref[T any](x T) *T { 410 + return &x 411 + }
+1 -6
internal/lsp/cache/package.go
··· 23 23 "cuelang.org/go/cue/ast" 24 24 "cuelang.org/go/cue/interpreter/embed" 25 25 "cuelang.org/go/cue/token" 26 - "cuelang.org/go/internal/core/runtime" 27 26 "cuelang.org/go/internal/golangorgx/gopls/protocol" 28 27 "cuelang.org/go/internal/lsp/eval" 29 28 "cuelang.org/go/internal/mod/modpkgload" ··· 366 365 // files are added and removed from the file system. This is 367 366 // not possible with imported packages: an import spec always 368 367 // refers to exactly one package. 369 - attrsByNode, err := runtime.ExtractAttrsByKind(syntax, embed.EmbedKind) 370 - if err != nil { 371 - errs = append(errs, err) 372 - } 373 - embeddedPaths, err := embed.EmbeddedPaths(modpkgFile.FilePath, attrsByNode) 368 + embeddedPaths, err := embed.EmbeddedPaths(modpkgFile.FilePath, syntax) 374 369 if err != nil { 375 370 errs = append(errs, err) 376 371 }
+5 -1
internal/lsp/eval/eval.go
··· 628 628 for remotePkg, embeds := range e.config.PkgEmbedders() { 629 629 remotePkg.bootFiles() 630 630 for _, embed := range embeds { 631 - remotePos := embed.Node.Pos() 631 + node := embed.Node 632 + if f, ok := node.(*ast.Field); ok { 633 + node = f.Value 634 + } 635 + remotePos := node.Pos() 632 636 remoteFilename := remotePos.Filename() 633 637 remoteFe := remotePkg.byFilename[remoteFilename] 634 638 if remoteFe == nil {