cuelang.org/go@v0.13.0/cue/ast/astutil/resolve.go (about)

     1  // Copyright 2018 The 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  // This file implements scopes and the objects they contain.
    16  
    17  package astutil
    18  
    19  import (
    20  	"fmt"
    21  	"strings"
    22  
    23  	"cuelang.org/go/cue/ast"
    24  	"cuelang.org/go/cue/token"
    25  )
    26  
    27  // An ErrFunc processes errors.
    28  type ErrFunc func(pos token.Pos, msg string, args ...interface{})
    29  
    30  // TODO: future development
    31  //
    32  // Resolution currently assigns values along the table below. This is based on
    33  // Go's resolver and is not quite convenient for CUE's purposes. For one, CUE
    34  // allows manually setting resolution and than call astutil.Sanitize to
    35  // normalize the ast.File. Manually assigning resolutions according to the
    36  // below table is rather tedious though.
    37  //
    38  // Instead of using the Scope and Node fields in identifiers, we suggest the
    39  // following assignments:
    40  //
    41  //    Reference Node // an Decl or Clause
    42  //    Ident *Ident   // The identifier in References (optional)
    43  //
    44  // References always refers to the direct element in the scope in which the
    45  // identifier occurs, not the final value, so: *Field, *LetClause, *ForClause,
    46  // etc. In case Ident is defined, it must be the same pointer as the
    47  // referencing identifier. In case it is not defined, the Name of the
    48  // referencing identifier can be used to locate the proper identifier in the
    49  // referenced node.
    50  //
    51  // The Scope field in the original design then loses its function.
    52  //
    53  // Type of reference      Scope          Node
    54  // Let Clause             File/Struct    LetClause
    55  // Alias declaration      File/Struct    Alias (deprecated)
    56  // Illegal Reference      File/Struct
    57  // Value
    58  //   X in a: X=y          Field          Alias
    59  // Fields
    60  //   X in X: y            File/Struct    Expr (y)
    61  //   X in X=x: y          File/Struct    Field
    62  //   X in X=(x): y        File/Struct    Field
    63  //   X in X="\(x)": y     File/Struct    Field
    64  //   X in [X=x]: y        Field          Expr (x)
    65  //   X in X=[x]: y        Field          Field
    66  //
    67  // for k, v in            ForClause      Ident
    68  // let x = y              LetClause      Ident
    69  //
    70  // Fields inside lambda
    71  //    Label               Field          Expr
    72  //    Value               Field          Field
    73  // Pkg                    nil            ImportSpec
    74  
    75  // Resolve resolves all identifiers in a file. Unresolved identifiers are
    76  // recorded in Unresolved. It will not overwrite already resolved values.
    77  func Resolve(f *ast.File, errFn ErrFunc) {
    78  	visitor := &scope{errFn: errFn, identFn: resolveIdent}
    79  	ast.Walk(f, visitor.Before, nil)
    80  }
    81  
    82  // Resolve resolves all identifiers in an expression.
    83  // It will not overwrite already resolved values.
    84  func ResolveExpr(e ast.Expr, errFn ErrFunc) {
    85  	f := &ast.File{}
    86  	visitor := &scope{file: f, errFn: errFn, identFn: resolveIdent}
    87  	ast.Walk(e, visitor.Before, nil)
    88  }
    89  
    90  // A Scope maintains the set of named language entities declared
    91  // in the scope and a link to the immediately surrounding (outer)
    92  // scope.
    93  type scope struct {
    94  	file    *ast.File
    95  	outer   *scope
    96  	node    ast.Node
    97  	index   map[string]entry
    98  	inField bool
    99  
   100  	identFn func(s *scope, n *ast.Ident) bool
   101  	nameFn  func(name string)
   102  	errFn   func(p token.Pos, msg string, args ...interface{})
   103  }
   104  
   105  type entry struct {
   106  	node ast.Node
   107  	link ast.Node // Alias, LetClause, or Field
   108  }
   109  
   110  func newScope(f *ast.File, outer *scope, node ast.Node, decls []ast.Decl) *scope {
   111  	const n = 4 // initial scope capacity
   112  	s := &scope{
   113  		file:    f,
   114  		outer:   outer,
   115  		node:    node,
   116  		index:   make(map[string]entry, n),
   117  		identFn: outer.identFn,
   118  		nameFn:  outer.nameFn,
   119  		errFn:   outer.errFn,
   120  	}
   121  	for _, d := range decls {
   122  		switch x := d.(type) {
   123  		case *ast.Field:
   124  			label := x.Label
   125  
   126  			if a, ok := x.Label.(*ast.Alias); ok {
   127  				name := a.Ident.Name
   128  				if _, ok := a.Expr.(*ast.ListLit); !ok {
   129  					s.insert(name, x, a)
   130  				}
   131  			}
   132  
   133  			// default:
   134  			name, isIdent, _ := ast.LabelName(label)
   135  			if isIdent {
   136  				v := x.Value
   137  				// Avoid interpreting value aliases at this point.
   138  				if a, ok := v.(*ast.Alias); ok {
   139  					v = a.Expr
   140  				}
   141  				s.insert(name, v, x)
   142  			}
   143  		case *ast.LetClause:
   144  			name, isIdent, _ := ast.LabelName(x.Ident)
   145  			if isIdent {
   146  				s.insert(name, x, x)
   147  			}
   148  		case *ast.Alias:
   149  			name, isIdent, _ := ast.LabelName(x.Ident)
   150  			if isIdent {
   151  				s.insert(name, x, x)
   152  			}
   153  		case *ast.ImportDecl:
   154  			for _, spec := range x.Specs {
   155  				info, _ := ParseImportSpec(spec)
   156  				s.insert(info.Ident, spec, spec)
   157  			}
   158  		}
   159  	}
   160  	return s
   161  }
   162  
   163  func (s *scope) isLet(n ast.Node) bool {
   164  	if _, ok := s.node.(*ast.Field); ok {
   165  		return true
   166  	}
   167  	switch n.(type) {
   168  	case *ast.LetClause, *ast.Alias, *ast.Field:
   169  		return true
   170  	}
   171  	return false
   172  }
   173  
   174  func (s *scope) mustBeUnique(n ast.Node) bool {
   175  	if _, ok := s.node.(*ast.Field); ok {
   176  		return true
   177  	}
   178  	switch n.(type) {
   179  	// TODO: add *ast.ImportSpec when some implementations are moved over to
   180  	// Sanitize.
   181  	case *ast.ImportSpec, *ast.LetClause, *ast.Alias, *ast.Field:
   182  		return true
   183  	}
   184  	return false
   185  }
   186  
   187  func (s *scope) insert(name string, n, link ast.Node) {
   188  	if name == "" {
   189  		return
   190  	}
   191  	if s.nameFn != nil {
   192  		s.nameFn(name)
   193  	}
   194  	// TODO: record both positions.
   195  	if outer, _, existing := s.lookup(name); existing.node != nil {
   196  		if s.isLet(n) != outer.isLet(existing.node) {
   197  			s.errFn(n.Pos(), "cannot have both alias and field with name %q in same scope", name)
   198  			return
   199  		} else if s.mustBeUnique(n) || outer.mustBeUnique(existing.node) {
   200  			if outer == s {
   201  				if _, ok := existing.node.(*ast.ImportSpec); ok {
   202  					return
   203  					// TODO:
   204  					// s.errFn(n.Pos(), "conflicting declaration %s\n"+
   205  					// 	"\tprevious declaration at %s",
   206  					// 	name, existing.node.Pos())
   207  				} else {
   208  					s.errFn(n.Pos(), "alias %q redeclared in same scope", name)
   209  				}
   210  				return
   211  			}
   212  			// TODO: Should we disallow shadowing of aliases?
   213  			// This was the case, but it complicates the transition to
   214  			// square brackets. The spec says allow it.
   215  			// s.errFn(n.Pos(), "alias %q already declared in enclosing scope", name)
   216  		}
   217  	}
   218  	s.index[name] = entry{node: n, link: link}
   219  }
   220  
   221  func (s *scope) resolveScope(name string, node ast.Node) (scope ast.Node, e entry, ok bool) {
   222  	last := s
   223  	for s != nil {
   224  		if n, ok := s.index[name]; ok && node == n.node {
   225  			if last.node == n.node {
   226  				return nil, n, true
   227  			}
   228  			return s.node, n, true
   229  		}
   230  		s, last = s.outer, s
   231  	}
   232  	return nil, entry{}, false
   233  }
   234  
   235  func (s *scope) lookup(name string) (p *scope, obj ast.Node, node entry) {
   236  	// TODO(#152): consider returning nil for obj if it is a reference to root.
   237  	// last := s
   238  	if name == "_" {
   239  		return nil, nil, entry{}
   240  	}
   241  	for s != nil {
   242  		if n, ok := s.index[name]; ok {
   243  			if _, ok := n.node.(*ast.ImportSpec); ok {
   244  				return s, nil, n
   245  			}
   246  			return s, s.node, n
   247  		}
   248  		// s, last = s.outer, s
   249  		s = s.outer
   250  	}
   251  	return nil, nil, entry{}
   252  }
   253  
   254  func (s *scope) Before(n ast.Node) bool {
   255  	switch x := n.(type) {
   256  	case *ast.File:
   257  		s := newScope(x, s, x, x.Decls)
   258  		// Support imports.
   259  		for _, d := range x.Decls {
   260  			ast.Walk(d, s.Before, nil)
   261  		}
   262  		return false
   263  
   264  	case *ast.StructLit:
   265  		s = newScope(s.file, s, x, x.Elts)
   266  		for _, elt := range x.Elts {
   267  			ast.Walk(elt, s.Before, nil)
   268  		}
   269  		return false
   270  
   271  	case *ast.Comprehension:
   272  		s = scopeClauses(s, x.Clauses)
   273  		ast.Walk(x.Value, s.Before, nil)
   274  		return false
   275  
   276  	case *ast.Field:
   277  		var n ast.Node = x.Label
   278  		alias, ok := x.Label.(*ast.Alias)
   279  		if ok {
   280  			n = alias.Expr
   281  		}
   282  
   283  		switch label := n.(type) {
   284  		case *ast.ParenExpr:
   285  			ast.Walk(label, s.Before, nil)
   286  
   287  		case *ast.Interpolation:
   288  			ast.Walk(label, s.Before, nil)
   289  
   290  		case *ast.ListLit:
   291  			if len(label.Elts) != 1 {
   292  				break
   293  			}
   294  			s = newScope(s.file, s, x, nil)
   295  			if alias != nil {
   296  				if name, _, _ := ast.LabelName(alias.Ident); name != "" {
   297  					s.insert(name, x, alias)
   298  				}
   299  			}
   300  
   301  			expr := label.Elts[0]
   302  
   303  			if a, ok := expr.(*ast.Alias); ok {
   304  				expr = a.Expr
   305  
   306  				// Add to current scope, instead of the value's, and allow
   307  				// references to bind to these illegally.
   308  				// We need this kind of administration anyway to detect
   309  				// illegal name clashes, and it allows giving better error
   310  				// messages. This puts the burden on clients of this library
   311  				// to detect illegal usage, though.
   312  				s.insert(a.Ident.Name, a.Expr, a)
   313  			}
   314  
   315  			ast.Walk(expr, nil, func(n ast.Node) {
   316  				if x, ok := n.(*ast.Ident); ok {
   317  					for s := s; s != nil && !s.inField; s = s.outer {
   318  						if _, ok := s.index[x.Name]; ok {
   319  							s.errFn(n.Pos(),
   320  								"reference %q in label expression refers to field against which it would be matched", x.Name)
   321  						}
   322  					}
   323  				}
   324  			})
   325  			ast.Walk(expr, s.Before, nil)
   326  		}
   327  
   328  		if n := x.Value; n != nil {
   329  			if alias, ok := x.Value.(*ast.Alias); ok {
   330  				// TODO: this should move into Before once decl attributes
   331  				// have been fully deprecated and embed attributes are introduced.
   332  				s = newScope(s.file, s, x, nil)
   333  				s.insert(alias.Ident.Name, alias, x)
   334  				n = alias.Expr
   335  			}
   336  			s.inField = true
   337  			ast.Walk(n, s.Before, nil)
   338  			s.inField = false
   339  		}
   340  
   341  		return false
   342  
   343  	case *ast.LetClause:
   344  		// Disallow referring to the current LHS name.
   345  		name := x.Ident.Name
   346  		saved := s.index[name]
   347  		delete(s.index, name) // The same name may still appear in another scope
   348  
   349  		if x.Expr != nil {
   350  			ast.Walk(x.Expr, s.Before, nil)
   351  		}
   352  		s.index[name] = saved
   353  		return false
   354  
   355  	case *ast.Alias:
   356  		// Disallow referring to the current LHS name.
   357  		name := x.Ident.Name
   358  		saved := s.index[name]
   359  		delete(s.index, name) // The same name may still appear in another scope
   360  
   361  		if x.Expr != nil {
   362  			ast.Walk(x.Expr, s.Before, nil)
   363  		}
   364  		s.index[name] = saved
   365  		return false
   366  
   367  	case *ast.ImportSpec:
   368  		return false
   369  
   370  	case *ast.Attribute:
   371  		// TODO: tokenize attributes, resolve identifiers and store the ones
   372  		// that resolve in a list.
   373  
   374  	case *ast.SelectorExpr:
   375  		ast.Walk(x.X, s.Before, nil)
   376  		return false
   377  
   378  	case *ast.Ident:
   379  		if s.identFn(s, x) {
   380  			return false
   381  		}
   382  	}
   383  	return true
   384  }
   385  
   386  func resolveIdent(s *scope, x *ast.Ident) bool {
   387  	name, ok, _ := ast.LabelName(x)
   388  	if !ok {
   389  		// TODO: generate error
   390  		return false
   391  	}
   392  	if _, obj, node := s.lookup(name); node.node != nil {
   393  		switch x.Node {
   394  		case nil:
   395  			x.Node = node.node
   396  			x.Scope = obj
   397  
   398  		case node.node:
   399  			x.Scope = obj
   400  
   401  		default: // x.Node != node
   402  			scope, _, ok := s.resolveScope(name, x.Node)
   403  			if !ok {
   404  				s.file.Unresolved = append(s.file.Unresolved, x)
   405  			}
   406  			x.Scope = scope
   407  		}
   408  	} else {
   409  		s.file.Unresolved = append(s.file.Unresolved, x)
   410  	}
   411  	return true
   412  }
   413  
   414  func scopeClauses(s *scope, clauses []ast.Clause) *scope {
   415  	for _, c := range clauses {
   416  		switch x := c.(type) {
   417  		case *ast.ForClause:
   418  			ast.Walk(x.Source, s.Before, nil)
   419  			s = newScope(s.file, s, x, nil)
   420  			if x.Key != nil {
   421  				s.insert(x.Key.Name, x.Key, x)
   422  			}
   423  			s.insert(x.Value.Name, x.Value, x)
   424  
   425  		case *ast.LetClause:
   426  			ast.Walk(x.Expr, s.Before, nil)
   427  			s = newScope(s.file, s, x, nil)
   428  			s.insert(x.Ident.Name, x.Ident, x)
   429  
   430  		default:
   431  			ast.Walk(c, s.Before, nil)
   432  		}
   433  	}
   434  	return s
   435  }
   436  
   437  // Debugging support
   438  func (s *scope) String() string {
   439  	var b strings.Builder
   440  	fmt.Fprintf(&b, "scope %p {", s)
   441  	if s != nil && len(s.index) > 0 {
   442  		fmt.Fprintln(&b)
   443  		for name := range s.index {
   444  			fmt.Fprintf(&b, "\t%v\n", name)
   445  		}
   446  	}
   447  	fmt.Fprintf(&b, "}\n")
   448  	return b.String()
   449  }