cuelang.org/go@v0.10.1/internal/core/dep/dep.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 dep analyzes dependencies between values.
    16  package dep
    17  
    18  import (
    19  	"cuelang.org/go/cue/errors"
    20  	"cuelang.org/go/internal/core/adt"
    21  )
    22  
    23  // Dependencies
    24  //
    25  // A dependency is a reference relation from one Vertex to another. A Vertex
    26  // has multiple Conjuncts, each of which is associated with an expression.
    27  // Each expression, in turn, may have multiple references, each representing
    28  // a single dependency.
    29  //
    30  // A reference that occurs in a node will point to another node. A reference
    31  // `x.y` may point to a node `x.y` as well as `x`. By default, only the most
    32  // precise node is reported, which is `x.y` if it exists, or `x` otherwise.
    33  // In the latter case, a path is associated with the reference to indicate
    34  // the specific non-existing path that is needed for that dependency. (TODO)
    35  //
    36  // A single reference may point to multiple nodes. For instance,
    37  // (a & b).z may point to both `a.z` and `b.z`. This has to be taken into
    38  // account if dep is used for substitutions.
    39  //
    40  //
    41  //   field: Conjunct
    42  //             |
    43  //           Expr                       Conjunct Expression
    44  //             |- Reference             A reference to led to a target
    45  //             |-    \- Target Node     Pointed to by Reference
    46  //             |-         \- UsedPath   The sole path used within Node
    47  
    48  // TODO: verify that these concepts are correctly reflected in the API:
    49  // Source:
    50  //     The CUE value for which dependencies are analyzed.
    51  //     This may differ per dependency for dynamic and transitive analysis.
    52  // Target:
    53  //     The field to which the found reference resolves.
    54  // Reference:
    55  //     The reference that resolved to the dependency.
    56  //     Replacing this reference in the conjuncts of the source vertex with a
    57  //     link to the target vertex yields the same result if there only a single
    58  //     dependency matching this reference.
    59  // Conjunct:
    60  //     The conjunct in which the Reference was found.
    61  // Used Path:
    62  //     The target vertex may be a parent of the actual, more precise,
    63  //     dependency, if the latter does not yet exist. The target path is the path
    64  //     from the target vertex to the actual dependency.
    65  // Trace:
    66  //     A sequence of dependencies leading to the result in case of transitive
    67  //     dependencies.
    68  
    69  // TODO: for a public API, a better approach seems to be to have a single
    70  // Visit method, with a configuration to set a bunch of orthogonal options.
    71  // Here are some examples of the options:
    72  //   - Dynamic:    evaluate and descend into computed fields.
    73  //   - Recurse:    evaluate dependencies of subfields as well.
    74  //   - Inner:      report dependencies within the root being visited.
    75  //   - RootLess:   report dependencies that do not have a path to the root.
    76  //   - Transitive: get all dependencies, not just the direct ones.
    77  //   - Substitute: do not get precise dependencies, but rather keep them
    78  //         such that each expression needs to be replaced with at most
    79  //         one dependency. Could be a method on Dependency.
    80  //   - ContinueOnError:  continue visiting even if there are errors.
    81  //   [add more as they come up]
    82  //
    83  
    84  type Config struct {
    85  	// Dynamic enables evaluting dependencies Vertex Arcs, recursively
    86  	Dynamic bool
    87  
    88  	// Descend enables recursively descending into fields. This option is
    89  	// implied by Dynamic.
    90  	Descend bool
    91  
    92  	// Cycles allows a Node to reported more than once. This includes the node
    93  	// passed to Visit, which is otherwise never reported. This option can be
    94  	// used to disable cycle checking. TODO: this is not yet implemented.
    95  	AllowCycles bool
    96  
    97  	// Rootless enables reporting nodes that do not have a path from the root.
    98  	// This includes variables of comprehensions and fields of composite literal
    99  	// values that are part of expressions, such as {out: v}.out.
   100  	Rootless bool
   101  
   102  	// TODO:
   103  	// ContinueOnError indicates whether to continue finding dependencies
   104  	// even when there are errors.
   105  	// ContinueOnError bool
   106  
   107  	//  pkg indicates the main package for which the analyzer is configured,
   108  	// which is used for reporting purposes.
   109  	Pkg *adt.ImportReference
   110  }
   111  
   112  // A Dependency is a reference and the node that reference resolves to.
   113  type Dependency struct {
   114  	// Node is the referenced node.
   115  	Node *adt.Vertex
   116  
   117  	// Reference is the expression that referenced the node.
   118  	Reference adt.Resolver
   119  
   120  	pkg *adt.ImportReference
   121  
   122  	top bool
   123  
   124  	visitor *visitor
   125  }
   126  
   127  // Recurse visits the dependencies of d.Node, using the same visit function as
   128  // the original.
   129  func (d *Dependency) Recurse() {
   130  	savedAll := d.visitor.all
   131  	savedTop := d.visitor.top
   132  	savedMarked := d.visitor.marked
   133  	d.visitor.all = d.visitor.recurse
   134  	d.visitor.top = true
   135  	d.visitor.marked = nil
   136  
   137  	d.visitor.visitReusingVisitor(d.Node, false)
   138  
   139  	d.visitor.all = savedAll
   140  	d.visitor.top = savedTop
   141  	d.visitor.marked = savedMarked
   142  }
   143  
   144  // Import returns the import reference or nil if the reference was within
   145  // the same package as the visited Vertex.
   146  func (d *Dependency) Import() *adt.ImportReference {
   147  	return d.pkg
   148  }
   149  
   150  // IsRoot reports whether the dependency is referenced by the root of the
   151  // original Vertex passed to any of the Visit* functions, and not one of its
   152  // descendent arcs. This always returns true for Visit().
   153  func (d *Dependency) IsRoot() bool {
   154  	return d.top
   155  }
   156  
   157  func importRef(r adt.Expr) *adt.ImportReference {
   158  	switch x := r.(type) {
   159  	case *adt.ImportReference:
   160  		return x
   161  	case *adt.SelectorExpr:
   162  		return importRef(x.X)
   163  	case *adt.IndexExpr:
   164  		return importRef(x.X)
   165  	}
   166  	return nil
   167  }
   168  
   169  // VisitFunc is used for reporting dependencies.
   170  type VisitFunc func(Dependency) error
   171  
   172  var empty *adt.Vertex
   173  
   174  func init() {
   175  	// TODO: Consider setting a non-nil BaseValue.
   176  	empty = &adt.Vertex{}
   177  	empty.ForceDone()
   178  }
   179  
   180  var zeroConfig = &Config{}
   181  
   182  // Visit calls f for the dependencies of n as determined by the given
   183  // configuration.
   184  func Visit(cfg *Config, c *adt.OpContext, n *adt.Vertex, f VisitFunc) error {
   185  	if cfg == nil {
   186  		cfg = zeroConfig
   187  	}
   188  	if c == nil {
   189  		panic("nil context")
   190  	}
   191  	v := visitor{
   192  		ctxt:       c,
   193  		fn:         f,
   194  		pkg:        cfg.Pkg,
   195  		recurse:    cfg.Descend,
   196  		all:        cfg.Descend,
   197  		top:        true,
   198  		cfgDynamic: cfg.Dynamic,
   199  	}
   200  	return v.visitReusingVisitor(n, true)
   201  }
   202  
   203  // visitReusingVisitor is factored out of Visit so that we may reuse visitor.
   204  func (v *visitor) visitReusingVisitor(n *adt.Vertex, top bool) error {
   205  	if v.cfgDynamic {
   206  		if v.marked == nil {
   207  			v.marked = marked{}
   208  		}
   209  		v.marked.markExpr(n)
   210  
   211  		v.dynamic(n, top)
   212  	} else {
   213  		v.visit(n, top)
   214  	}
   215  	return v.err
   216  }
   217  
   218  func (v *visitor) visit(n *adt.Vertex, top bool) (err error) {
   219  	savedNode := v.node
   220  	savedTop := v.top
   221  
   222  	v.node = n
   223  	v.top = top
   224  
   225  	defer func() {
   226  		v.node = savedNode
   227  		v.top = savedTop
   228  
   229  		switch x := recover(); x {
   230  		case nil:
   231  		case aborted:
   232  			err = v.err
   233  		default:
   234  			panic(x)
   235  		}
   236  	}()
   237  
   238  	n.VisitLeafConjuncts(func(x adt.Conjunct) bool {
   239  		v.markExpr(x.Env, x.Elem())
   240  		return true
   241  	})
   242  
   243  	return nil
   244  }
   245  
   246  var aborted = errors.New("aborted")
   247  
   248  type visitor struct {
   249  	ctxt *adt.OpContext
   250  	fn   VisitFunc
   251  	node *adt.Vertex
   252  	err  error
   253  	pkg  *adt.ImportReference
   254  
   255  	// recurse indicates whether, during static analysis, to process references
   256  	// that will be unified into different fields.
   257  	recurse bool
   258  	// all indicates wether to process references that would be unified into
   259  	// different fields. This similar to recurse, but sometimes gets temporarily
   260  	// overridden to deal with special cases.
   261  	all       bool
   262  	top       bool
   263  	topRef    adt.Resolver
   264  	pathStack []refEntry
   265  	numRefs   int // count of reported dependencies
   266  
   267  	// cfgDynamic is kept from the original config.
   268  	cfgDynamic bool
   269  
   270  	marked marked
   271  }
   272  
   273  type refEntry struct {
   274  	env *adt.Environment
   275  	ref adt.Resolver
   276  }
   277  
   278  // TODO: factor out the below logic as either a low-level dependency analyzer or
   279  // some walk functionality.
   280  
   281  // markExpr visits all nodes in an expression to mark dependencies.
   282  func (c *visitor) markExpr(env *adt.Environment, expr adt.Elem) {
   283  	if expr, ok := expr.(adt.Resolver); ok {
   284  		c.markResolver(env, expr)
   285  		return
   286  	}
   287  
   288  	saved := c.topRef
   289  	c.topRef = nil
   290  	defer func() { c.topRef = saved }()
   291  
   292  	switch x := expr.(type) {
   293  	case nil:
   294  	case *adt.BinaryExpr:
   295  		c.markExpr(env, x.X)
   296  		c.markExpr(env, x.Y)
   297  
   298  	case *adt.UnaryExpr:
   299  		c.markExpr(env, x.X)
   300  
   301  	case *adt.Interpolation:
   302  		for i := 1; i < len(x.Parts); i += 2 {
   303  			c.markExpr(env, x.Parts[i])
   304  		}
   305  
   306  	case *adt.BoundExpr:
   307  		c.markExpr(env, x.Expr)
   308  
   309  	case *adt.CallExpr:
   310  		c.markExpr(env, x.Fun)
   311  		saved := c.all
   312  		c.all = true
   313  		for _, a := range x.Args {
   314  			c.markExpr(env, a)
   315  		}
   316  		c.all = saved
   317  
   318  	case *adt.DisjunctionExpr:
   319  		for _, d := range x.Values {
   320  			c.markExpr(env, d.Val)
   321  		}
   322  
   323  	case *adt.SliceExpr:
   324  		c.markExpr(env, x.X)
   325  		c.markExpr(env, x.Lo)
   326  		c.markExpr(env, x.Hi)
   327  		c.markExpr(env, x.Stride)
   328  
   329  	case *adt.ListLit:
   330  		env := &adt.Environment{Up: env, Vertex: empty}
   331  		for _, e := range x.Elems {
   332  			switch x := e.(type) {
   333  			case *adt.Comprehension:
   334  				c.markComprehension(env, x)
   335  
   336  			case adt.Expr:
   337  				c.markSubExpr(env, x)
   338  
   339  			case *adt.Ellipsis:
   340  				if x.Value != nil {
   341  					c.markSubExpr(env, x.Value)
   342  				}
   343  			}
   344  		}
   345  
   346  	case *adt.StructLit:
   347  		env := &adt.Environment{Up: env, Vertex: empty}
   348  		for _, e := range x.Decls {
   349  			c.markDecl(env, e)
   350  		}
   351  
   352  	case *adt.Comprehension:
   353  		c.markComprehension(env, x)
   354  	}
   355  }
   356  
   357  // markResolve resolves dependencies.
   358  func (c *visitor) markResolver(env *adt.Environment, r adt.Resolver) {
   359  	// Note: it is okay to pass an empty CloseInfo{} here as we assume that
   360  	// all nodes are finalized already and we need neither closedness nor cycle
   361  	// checks.
   362  	ref, _ := c.ctxt.Resolve(adt.MakeConjunct(env, r, adt.CloseInfo{}), r)
   363  
   364  	// TODO: consider the case where an inlined composite literal does not
   365  	// resolve, but has references. For instance, {a: k, ref}.b would result
   366  	// in a failure during evaluation if b is not defined within ref. However,
   367  	// ref might still specialize to allow b.
   368  
   369  	if ref != nil {
   370  		c.reportDependency(env, r, ref)
   371  		return
   372  	}
   373  
   374  	// It is possible that a reference cannot be resolved because it is
   375  	// incomplete. In this case, we should check whether subexpressions of the
   376  	// reference can be resolved to mark those dependencies. For instance,
   377  	// prefix paths of selectors and the value or index of an index expression
   378  	// may independently resolve to a valid dependency.
   379  
   380  	switch x := r.(type) {
   381  	case *adt.NodeLink:
   382  		panic("unreachable")
   383  
   384  	case *adt.IndexExpr:
   385  		c.markExpr(env, x.X)
   386  		c.markExpr(env, x.Index)
   387  
   388  	case *adt.SelectorExpr:
   389  		c.markExpr(env, x.X)
   390  	}
   391  }
   392  
   393  // reportDependency reports a dependency from r to v.
   394  // v must be the value that is obtained after resolving r.
   395  func (c *visitor) reportDependency(env *adt.Environment, ref adt.Resolver, v *adt.Vertex) {
   396  	if v == c.node || v == empty {
   397  		return
   398  	}
   399  
   400  	reference := ref
   401  	if c.topRef == nil && len(c.pathStack) == 0 {
   402  		saved := c.topRef
   403  		c.topRef = ref
   404  		defer func() { c.topRef = saved }()
   405  	}
   406  
   407  	// TODO: in "All" mode we still report the latest reference used, instead
   408  	// of the reference at the start of the traversal, as the self-contained
   409  	// algorithm (its only user) depends on it.
   410  	// However, if the stack is non-nil, the reference will not correctly
   411  	// reflect the substituted value, so we use the top reference instead.
   412  	if !c.recurse && len(c.pathStack) == 0 && c.topRef != nil {
   413  		reference = c.topRef
   414  	}
   415  
   416  	if !v.Rooted() {
   417  		before := c.numRefs
   418  		c.markInternalResolvers(env, ref, v)
   419  		// TODO: this logic could probably be simplified if we let clients
   420  		// explicitly mark whether to visit rootless nodes. Visiting these
   421  		// may be necessary when substituting values.
   422  		switch _, ok := ref.(*adt.FieldReference); {
   423  		case !ok:
   424  			// Do not report rootless nodes for selectors.
   425  			return
   426  		case c.numRefs > before:
   427  			// For FieldReferences that resolve to something we do not need
   428  			// to report anything intermediate.
   429  			return
   430  		}
   431  	}
   432  	if hasLetParent(v) {
   433  		return
   434  	}
   435  
   436  	// Expand path.
   437  	altRef := reference
   438  	for i := len(c.pathStack) - 1; i >= 0; i-- {
   439  		x := c.pathStack[i]
   440  		var w *adt.Vertex
   441  		// TODO: instead of setting the reference, the proper thing to do is
   442  		// to record a path that still needs to be selected into the recorded
   443  		// dependency. See the Target Path definition at the top of the file.
   444  		if f := c.feature(x.env, x.ref); f != 0 {
   445  			w = v.Lookup(f)
   446  		}
   447  		if w == nil {
   448  			break
   449  		}
   450  		altRef = x.ref
   451  		if i == 0 && c.topRef != nil {
   452  			altRef = c.topRef
   453  		}
   454  		v = w
   455  	}
   456  
   457  	// All resolvers are expressions.
   458  	if p := importRef(ref.(adt.Expr)); p != nil {
   459  		savedPkg := c.pkg
   460  		c.pkg = p
   461  		defer func() { c.pkg = savedPkg }()
   462  	}
   463  
   464  	c.numRefs++
   465  
   466  	d := Dependency{
   467  		Node:      v,
   468  		Reference: altRef,
   469  		pkg:       c.pkg,
   470  		top:       c.top,
   471  		visitor:   c,
   472  	}
   473  	if err := c.fn(d); err != nil {
   474  		c.err = err
   475  		panic(aborted)
   476  	}
   477  }
   478  
   479  // TODO(perf): make this available as a property of vertices to avoid doing
   480  // work repeatedly.
   481  func hasLetParent(v *adt.Vertex) bool {
   482  	for ; v != nil; v = v.Parent {
   483  		if v.Label.IsLet() {
   484  			return true
   485  		}
   486  	}
   487  	return false
   488  }
   489  
   490  // markConjuncts transitively marks all reference of the current node.
   491  func (c *visitor) markConjuncts(v *adt.Vertex) {
   492  	v.VisitLeafConjuncts(func(x adt.Conjunct) bool {
   493  		// Use Elem instead of Expr to preserve the Comprehension to, in turn,
   494  		// ensure an Environment is inserted for the Value clause.
   495  		c.markExpr(x.Env, x.Elem())
   496  		return true
   497  	})
   498  }
   499  
   500  // markInternalResolvers marks dependencies for rootless nodes. As these
   501  // nodes may not be visited during normal traversal, we need to be more
   502  // proactive. For selectors and indices this means we need to evaluate their
   503  // objects to see exactly what the selector or index refers to.
   504  func (c *visitor) markInternalResolvers(env *adt.Environment, r adt.Resolver, v *adt.Vertex) {
   505  	if v.Rooted() {
   506  		panic("node must not be rooted")
   507  	}
   508  
   509  	saved := c.all // recursive traversal already done by this function.
   510  
   511  	// As lets have no path and we otherwise will not process them, we set
   512  	// processing all to true.
   513  	if c.marked != nil && hasLetParent(v) {
   514  		v.VisitLeafConjuncts(func(x adt.Conjunct) bool {
   515  			c.marked.markExpr(x.Expr())
   516  			return true
   517  		})
   518  	}
   519  
   520  	c.markConjuncts(v)
   521  
   522  	// evaluateInner will already process all values recursively, so disable
   523  	// while processing in this case.
   524  	c.all = false
   525  
   526  	switch r := r.(type) {
   527  	case *adt.SelectorExpr:
   528  		c.evaluateInner(env, r.X, r)
   529  	case *adt.IndexExpr:
   530  		c.evaluateInner(env, r.X, r)
   531  	}
   532  
   533  	c.all = saved
   534  }
   535  
   536  // evaluateInner evaluates the LHS of the given selector or index expression,
   537  // and marks all its conjuncts. The reference is pushed on a stack to mark
   538  // the field or index that needs to be selected for any dependencies that are
   539  // subsequently encountered. This is handled by reportDependency.
   540  func (c *visitor) evaluateInner(env *adt.Environment, x adt.Expr, r adt.Resolver) {
   541  	value, _ := c.ctxt.Evaluate(env, x)
   542  	v, _ := value.(*adt.Vertex)
   543  	if v == nil {
   544  		return
   545  	}
   546  	// TODO(perf): one level of  evaluation would suffice.
   547  	v.Finalize(c.ctxt)
   548  
   549  	saved := len(c.pathStack)
   550  	c.pathStack = append(c.pathStack, refEntry{env, r})
   551  	c.markConjuncts(v)
   552  	c.pathStack = c.pathStack[:saved]
   553  }
   554  
   555  func (c *visitor) feature(env *adt.Environment, r adt.Resolver) adt.Feature {
   556  	switch r := r.(type) {
   557  	case *adt.SelectorExpr:
   558  		return r.Sel
   559  	case *adt.IndexExpr:
   560  		v, _ := c.ctxt.Evaluate(env, r.Index)
   561  		v = adt.Unwrap(v)
   562  		return adt.LabelFromValue(c.ctxt, r.Index, v)
   563  	default:
   564  		return adt.InvalidLabel
   565  	}
   566  }
   567  
   568  func (c *visitor) markSubExpr(env *adt.Environment, x adt.Expr) {
   569  	if c.all {
   570  		saved := c.top
   571  		c.top = false
   572  		c.markExpr(env, x)
   573  		c.top = saved
   574  	}
   575  }
   576  
   577  func (c *visitor) markDecl(env *adt.Environment, d adt.Decl) {
   578  	switch x := d.(type) {
   579  	case *adt.Field:
   580  		c.markSubExpr(env, x.Value)
   581  
   582  	case *adt.BulkOptionalField:
   583  		c.markExpr(env, x.Filter)
   584  		// when dynamic, only continue if there is evidence of
   585  		// the field in the parallel actual evaluation.
   586  		c.markSubExpr(env, x.Value)
   587  
   588  	case *adt.DynamicField:
   589  		c.markExpr(env, x.Key)
   590  		// when dynamic, only continue if there is evidence of
   591  		// a matching field in the parallel actual evaluation.
   592  		c.markSubExpr(env, x.Value)
   593  
   594  	case *adt.Comprehension:
   595  		c.markComprehension(env, x)
   596  
   597  	case adt.Expr:
   598  		c.markExpr(env, x)
   599  
   600  	case *adt.Ellipsis:
   601  		if x.Value != nil {
   602  			c.markSubExpr(env, x.Value)
   603  		}
   604  	}
   605  }
   606  
   607  func (c *visitor) markComprehension(env *adt.Environment, y *adt.Comprehension) {
   608  	env = c.markClauses(env, y.Clauses)
   609  
   610  	// Use "live" environments if we have them. This is important if
   611  	// dependencies are computed on a partially evaluated value where a pushed
   612  	// down comprehension is defined outside the root of the dependency
   613  	// analysis. For instance, when analyzing dependencies at path a.b in:
   614  	//
   615  	//  a: {
   616  	//      for value in { test: 1 } {
   617  	//          b: bar: value
   618  	//      }
   619  	//  }
   620  	//
   621  	if envs := y.Envs(); len(envs) > 0 {
   622  		// We use the Environment to get access to the parent chain. It
   623  		// suffices to take any Environment (in this case the first), as all
   624  		// will have the same parent chain.
   625  		env = envs[0]
   626  	}
   627  	for i := y.Nest(); i > 0; i-- {
   628  		env = &adt.Environment{Up: env, Vertex: empty}
   629  	}
   630  	// TODO: consider using adt.EnvExpr and remove the above loop.
   631  	c.markExpr(env, adt.ToExpr(y.Value))
   632  }
   633  
   634  func (c *visitor) markClauses(env *adt.Environment, a []adt.Yielder) *adt.Environment {
   635  	for _, y := range a {
   636  		switch x := y.(type) {
   637  		case *adt.ForClause:
   638  			c.markExpr(env, x.Src)
   639  			env = &adt.Environment{Up: env, Vertex: empty}
   640  			// In dynamic mode, iterate over all actual value and
   641  			// evaluate.
   642  
   643  		case *adt.LetClause:
   644  			c.markExpr(env, x.Expr)
   645  			env = &adt.Environment{Up: env, Vertex: empty}
   646  
   647  		case *adt.IfClause:
   648  			c.markExpr(env, x.Condition)
   649  			// In dynamic mode, only continue if condition is true.
   650  
   651  		case *adt.ValueClause:
   652  			env = &adt.Environment{Up: env, Vertex: empty}
   653  		}
   654  	}
   655  	return env
   656  }