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