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