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