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