| // Copyright 2020 CUE Authors |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| |
| package runtime |
| |
| import ( |
| "path" |
| "strconv" |
| |
| "cuelang.org/go/cue/ast" |
| "cuelang.org/go/cue/build" |
| "cuelang.org/go/cue/errors" |
| ) |
| |
| func lineStr(idx *Index, n ast.Node) string { |
| return n.Pos().String() |
| } |
| |
| func ResolveFiles( |
| idx *Index, |
| p *build.Instance, |
| isBuiltin func(s string) bool, |
| ) errors.Error { |
| // Link top-level declarations. As top-level entries get unified, an entry |
| // may be linked to any top-level entry of any of the files. |
| allFields := map[string]ast.Node{} |
| for _, file := range p.Files { |
| for _, d := range file.Decls { |
| if f, ok := d.(*ast.Field); ok && f.Value != nil { |
| if ident, ok := f.Label.(*ast.Ident); ok { |
| allFields[ident.Name] = f.Value |
| } |
| } |
| } |
| } |
| for _, f := range p.Files { |
| if err := ResolveFile(idx, f, p, allFields, isBuiltin); err != nil { |
| return err |
| } |
| } |
| return nil |
| } |
| |
| func ResolveFile( |
| idx *Index, |
| f *ast.File, |
| p *build.Instance, |
| allFields map[string]ast.Node, |
| isBuiltin func(s string) bool, |
| ) errors.Error { |
| unresolved := map[string][]*ast.Ident{} |
| for _, u := range f.Unresolved { |
| unresolved[u.Name] = append(unresolved[u.Name], u) |
| } |
| fields := map[string]ast.Node{} |
| for _, d := range f.Decls { |
| if f, ok := d.(*ast.Field); ok && f.Value != nil { |
| if ident, ok := f.Label.(*ast.Ident); ok { |
| fields[ident.Name] = d |
| } |
| } |
| } |
| var errs errors.Error |
| |
| specs := []*ast.ImportSpec{} |
| |
| for _, spec := range f.Imports { |
| id, err := strconv.Unquote(spec.Path.Value) |
| if err != nil { |
| continue // quietly ignore the error |
| } |
| name := path.Base(id) |
| if imp := p.LookupImport(id); imp != nil { |
| name = imp.PkgName |
| } else if !isBuiltin(id) { |
| errs = errors.Append(errs, |
| nodeErrorf(spec, "package %q not found", id)) |
| continue |
| } |
| if spec.Name != nil { |
| name = spec.Name.Name |
| } |
| if n, ok := fields[name]; ok { |
| errs = errors.Append(errs, nodeErrorf(spec, |
| "%s redeclared as imported package name\n"+ |
| "\tprevious declaration at %v", name, lineStr(idx, n))) |
| continue |
| } |
| fields[name] = spec |
| used := false |
| for _, u := range unresolved[name] { |
| used = true |
| u.Node = spec |
| } |
| if !used { |
| specs = append(specs, spec) |
| } |
| } |
| |
| // Verify each import is used. |
| if len(specs) > 0 { |
| // Find references to imports. This assumes that identifiers in labels |
| // are not resolved or that such errors are caught elsewhere. |
| ast.Walk(f, nil, func(n ast.Node) { |
| if x, ok := n.(*ast.Ident); ok { |
| // As we also visit labels, most nodes will be nil. |
| if x.Node == nil { |
| return |
| } |
| for i, s := range specs { |
| if s == x.Node { |
| specs[i] = nil |
| return |
| } |
| } |
| } |
| }) |
| |
| // Add errors for unused imports. |
| for _, spec := range specs { |
| if spec == nil { |
| continue |
| } |
| if spec.Name == nil { |
| errs = errors.Append(errs, nodeErrorf(spec, |
| "imported and not used: %s", spec.Path.Value)) |
| } else { |
| errs = errors.Append(errs, nodeErrorf(spec, |
| "imported and not used: %s as %s", spec.Path.Value, spec.Name)) |
| } |
| } |
| } |
| |
| k := 0 |
| for _, u := range f.Unresolved { |
| if u.Node != nil { |
| continue |
| } |
| if n, ok := allFields[u.Name]; ok { |
| u.Node = n |
| u.Scope = f |
| continue |
| } |
| f.Unresolved[k] = u |
| k++ |
| } |
| f.Unresolved = f.Unresolved[:k] |
| // TODO: also need to resolve types. |
| // if len(f.Unresolved) > 0 { |
| // n := f.Unresolved[0] |
| // return ctx.mkErr(newBase(n), "unresolved reference %s", n.Name) |
| // } |
| return errs |
| } |