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