...

Source file src/cuelang.org/go/internal/core/runtime/resolve.go

Documentation: cuelang.org/go/internal/core/runtime

     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 runtime
    16  
    17  import (
    18  	"path"
    19  	"strconv"
    20  
    21  	"cuelang.org/go/cue/ast"
    22  	"cuelang.org/go/cue/build"
    23  	"cuelang.org/go/cue/errors"
    24  	"cuelang.org/go/internal"
    25  )
    26  
    27  // TODO(resolve): this is also done in compile, do we need both?
    28  func (r *Runtime) ResolveFiles(p *build.Instance) (errs errors.Error) {
    29  	idx := r.index
    30  
    31  	// Link top-level declarations. As top-level entries get unified, an entry
    32  	// may be linked to any top-level entry of any of the files.
    33  	allFields := map[string]ast.Node{}
    34  	for _, f := range p.Files {
    35  		if p := internal.GetPackageInfo(f); p.IsAnonymous() {
    36  			continue
    37  		}
    38  		for _, d := range f.Decls {
    39  			if f, ok := d.(*ast.Field); ok && f.Value != nil {
    40  				if ident, ok := f.Label.(*ast.Ident); ok {
    41  					allFields[ident.Name] = f.Value
    42  				}
    43  			}
    44  		}
    45  	}
    46  	for _, f := range p.Files {
    47  		if p := internal.GetPackageInfo(f); p.IsAnonymous() {
    48  			continue
    49  		}
    50  		err := resolveFile(idx, f, p, allFields)
    51  		errs = errors.Append(errs, err)
    52  	}
    53  	return errs
    54  }
    55  
    56  func resolveFile(
    57  	idx *index,
    58  	f *ast.File,
    59  	p *build.Instance,
    60  	allFields map[string]ast.Node,
    61  ) errors.Error {
    62  	unresolved := map[string][]*ast.Ident{}
    63  	for _, u := range f.Unresolved {
    64  		unresolved[u.Name] = append(unresolved[u.Name], u)
    65  	}
    66  	fields := map[string]ast.Node{}
    67  	for _, d := range f.Decls {
    68  		if f, ok := d.(*ast.Field); ok && f.Value != nil {
    69  			if ident, ok := f.Label.(*ast.Ident); ok {
    70  				fields[ident.Name] = d
    71  			}
    72  		}
    73  	}
    74  	var errs errors.Error
    75  
    76  	specs := []*ast.ImportSpec{}
    77  
    78  	for _, spec := range f.Imports {
    79  		id, err := strconv.Unquote(spec.Path.Value)
    80  		if err != nil {
    81  			continue // quietly ignore the error
    82  		}
    83  		name := path.Base(id)
    84  		if imp := p.LookupImport(id); imp != nil {
    85  			name = imp.PkgName
    86  		} else if _, ok := idx.builtinPaths[id]; !ok {
    87  			errs = errors.Append(errs,
    88  				nodeErrorf(spec, "package %q not found", id))
    89  			continue
    90  		}
    91  		if spec.Name != nil {
    92  			name = spec.Name.Name
    93  		}
    94  		if n, ok := fields[name]; ok {
    95  			errs = errors.Append(errs, nodeErrorf(spec,
    96  				"%s redeclared as imported package name\n"+
    97  					"\tprevious declaration at %v", name, lineStr(idx, n)))
    98  			continue
    99  		}
   100  		fields[name] = spec
   101  		used := false
   102  		for _, u := range unresolved[name] {
   103  			used = true
   104  			u.Node = spec
   105  		}
   106  		if !used {
   107  			specs = append(specs, spec)
   108  		}
   109  	}
   110  
   111  	// Verify each import is used.
   112  	if len(specs) > 0 {
   113  		// Find references to imports. This assumes that identifiers in labels
   114  		// are not resolved or that such errors are caught elsewhere.
   115  		ast.Walk(f, nil, func(n ast.Node) {
   116  			if x, ok := n.(*ast.Ident); ok {
   117  				// As we also visit labels, most nodes will be nil.
   118  				if x.Node == nil {
   119  					return
   120  				}
   121  				for i, s := range specs {
   122  					if s == x.Node {
   123  						specs[i] = nil
   124  						return
   125  					}
   126  				}
   127  			}
   128  		})
   129  
   130  		// Add errors for unused imports.
   131  		for _, spec := range specs {
   132  			if spec == nil {
   133  				continue
   134  			}
   135  			if spec.Name == nil {
   136  				errs = errors.Append(errs, nodeErrorf(spec,
   137  					"imported and not used: %s", spec.Path.Value))
   138  			} else {
   139  				errs = errors.Append(errs, nodeErrorf(spec,
   140  					"imported and not used: %s as %s", spec.Path.Value, spec.Name))
   141  			}
   142  		}
   143  	}
   144  
   145  	k := 0
   146  	for _, u := range f.Unresolved {
   147  		if u.Node != nil {
   148  			continue
   149  		}
   150  		if n, ok := allFields[u.Name]; ok {
   151  			u.Node = n
   152  			u.Scope = f
   153  			continue
   154  		}
   155  		f.Unresolved[k] = u
   156  		k++
   157  	}
   158  	f.Unresolved = f.Unresolved[:k]
   159  	// TODO: also need to resolve types.
   160  	// if len(f.Unresolved) > 0 {
   161  	// 	n := f.Unresolved[0]
   162  	// 	return ctx.mkErr(newBase(n), "unresolved reference %s", n.Name)
   163  	// }
   164  	return errs
   165  }
   166  
   167  func lineStr(idx *index, n ast.Node) string {
   168  	return n.Pos().String()
   169  }
   170  

View as plain text