blob: 94188dbb1376f10ce6d1e4b1d0188ae1dae800a7 [file] [log] [blame]
Marcel van Lohuizenb2ea6482020-06-21 18:54:47 +02001// 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
15package runtime
16
17import (
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 Lohuizenb5821dc2020-10-11 13:57:13 +020026func (r *Runtime) ResolveFiles(p *build.Instance) (errs errors.Error) {
27 idx := r.index
Marcel van Lohuizenb2ea6482020-06-21 18:54:47 +020028
Marcel van Lohuizenb2ea6482020-06-21 18:54:47 +020029 // 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 Lohuizenb5821dc2020-10-11 13:57:13 +020042 err := resolveFile(idx, f, p, allFields)
Marcel van Lohuizen60c207f2020-09-12 19:48:58 +020043 errs = errors.Append(errs, err)
Marcel van Lohuizenb2ea6482020-06-21 18:54:47 +020044 }
Marcel van Lohuizen60c207f2020-09-12 19:48:58 +020045 return errs
Marcel van Lohuizenb2ea6482020-06-21 18:54:47 +020046}
47
Marcel van Lohuizenb5821dc2020-10-11 13:57:13 +020048func resolveFile(
49 idx *index,
Marcel van Lohuizenb2ea6482020-06-21 18:54:47 +020050 f *ast.File,
51 p *build.Instance,
52 allFields map[string]ast.Node,
Marcel van Lohuizenb2ea6482020-06-21 18:54:47 +020053) 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 Lohuizenb5821dc2020-10-11 13:57:13 +020078 } else if _, ok := idx.builtins[id]; !ok {
Marcel van Lohuizenb2ea6482020-06-21 18:54:47 +020079 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 Lohuizenb5821dc2020-10-11 13:57:13 +0200158
159func lineStr(idx *index, n ast.Node) string {
160 return n.Pos().String()
161}