this repo has no description
at master 159 lines 4.0 kB view raw
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 15package runtime 16 17import ( 18 "path" 19 "slices" 20 "strconv" 21 22 "cuelang.org/go/cue/ast" 23 "cuelang.org/go/cue/build" 24 "cuelang.org/go/cue/errors" 25) 26 27// TODO(resolve): this is also done in compile, do we need both? 28func (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 f.PackageName() == "" { 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 err := resolveFile(idx, f, p, allFields) 48 errs = errors.Append(errs, err) 49 } 50 return errs 51} 52 53func resolveFile( 54 idx *index, 55 f *ast.File, 56 p *build.Instance, 57 allFields map[string]ast.Node, 58) errors.Error { 59 unresolved := map[string][]*ast.Ident{} 60 for _, u := range f.Unresolved { 61 unresolved[u.Name] = append(unresolved[u.Name], u) 62 } 63 fields := map[string]ast.Node{} 64 for _, d := range f.Decls { 65 if f, ok := d.(*ast.Field); ok && f.Value != nil { 66 if ident, ok := f.Label.(*ast.Ident); ok { 67 fields[ident.Name] = d 68 } 69 } 70 } 71 var errs errors.Error 72 73 specs := []*ast.ImportSpec{} 74 75 for spec := range f.ImportSpecs() { 76 id, err := strconv.Unquote(spec.Path.Value) 77 if err != nil { 78 continue // quietly ignore the error 79 } 80 name := path.Base(id) 81 if imp := p.LookupImport(id); imp != nil { 82 name = imp.PkgName 83 } else if _, ok := idx.builtinPaths[id]; !ok { 84 errs = errors.Append(errs, 85 nodeErrorf(spec, "package %q not found", id)) 86 continue 87 } 88 if spec.Name != nil { 89 name = spec.Name.Name 90 } 91 if n, ok := fields[name]; ok { 92 errs = errors.Append(errs, nodeErrorf(spec, 93 "%s redeclared as imported package name\n"+ 94 "\tprevious declaration at %s", name, n.Pos())) 95 continue 96 } 97 fields[name] = spec 98 used := false 99 for _, u := range unresolved[name] { 100 used = true 101 u.Node = spec 102 } 103 if !used { 104 specs = append(specs, spec) 105 } 106 } 107 108 // Verify each import is used. 109 if len(specs) > 0 { 110 // Find references to imports. This assumes that identifiers in labels 111 // are not resolved or that such errors are caught elsewhere. 112 ast.Walk(f, nil, func(n ast.Node) { 113 if x, ok := n.(*ast.Ident); ok { 114 // As we also visit labels, most nodes will be nil. 115 if x.Node == nil { 116 return 117 } 118 for i, s := range specs { 119 if s == x.Node { 120 specs[i] = nil 121 return 122 } 123 } 124 } 125 }) 126 127 // Add errors for unused imports. 128 for _, spec := range specs { 129 if spec == nil { 130 continue 131 } 132 if spec.Name == nil { 133 errs = errors.Append(errs, nodeErrorf(spec, 134 "imported and not used: %s", spec.Path.Value)) 135 } else { 136 errs = errors.Append(errs, nodeErrorf(spec, 137 "imported and not used: %s as %s", spec.Path.Value, spec.Name)) 138 } 139 } 140 } 141 142 f.Unresolved = slices.DeleteFunc(f.Unresolved, func(u *ast.Ident) bool { 143 if u.Node != nil { 144 return true 145 } 146 if n, ok := allFields[u.Name]; ok { 147 u.Node = n 148 u.Scope = f 149 return true 150 } 151 return false 152 }) 153 // TODO: also need to resolve types. 154 // if len(f.Unresolved) > 0 { 155 // n := f.Unresolved[0] 156 // return ctx.mkErr(newBase(n), "unresolved reference %s", n.Name) 157 // } 158 return errs 159}