1// Copyright 2019 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 format 16 17import ( 18 "strconv" 19 20 "cuelang.org/go/cue/ast" 21 "cuelang.org/go/cue/ast/astutil" 22 "cuelang.org/go/internal" 23) 24 25// labelSimplifier rewrites string labels to identifiers if 26// no identifiers will subsequently bind to the exposed label. 27// In other words, string labels are only replaced if this does 28// not change the semantics of the CUE code. 29type labelSimplifier struct { 30 parent *labelSimplifier 31 scope map[string]bool 32} 33 34func (s *labelSimplifier) processDecls(decls []ast.Decl) { 35 sc := labelSimplifier{parent: s, scope: map[string]bool{}} 36 for _, d := range decls { 37 switch x := d.(type) { 38 case *ast.Field: 39 ast.Walk(x.Label, sc.markStrings, nil) 40 } 41 } 42 43 for _, d := range decls { 44 switch x := d.(type) { 45 case *ast.Field: 46 ast.Walk(x.Value, sc.markReferences, nil) 47 default: 48 ast.Walk(x, sc.markReferences, nil) 49 } 50 } 51 52 for _, d := range decls { 53 switch x := d.(type) { 54 case *ast.Field: 55 x.Label = astutil.Apply(x.Label, sc.replace, nil).(ast.Label) 56 } 57 } 58} 59 60func (s *labelSimplifier) markReferences(n ast.Node) bool { 61 // Record strings at this level. 62 switch x := n.(type) { 63 case *ast.File: 64 s.processDecls(x.Decls) 65 return false 66 67 case *ast.StructLit: 68 s.processDecls(x.Elts) 69 return false 70 71 case *ast.SelectorExpr: 72 ast.Walk(x.X, s.markReferences, nil) 73 return false 74 75 case *ast.Ident: 76 for c := s; c != nil; c = c.parent { 77 if _, ok := c.scope[x.Name]; ok { 78 c.scope[x.Name] = false 79 break 80 } 81 } 82 } 83 return true 84} 85 86func (s *labelSimplifier) markStrings(n ast.Node) bool { 87 switch x := n.(type) { 88 case *ast.BasicLit: 89 str, err := strconv.Unquote(x.Value) 90 if err != nil || !ast.IsValidIdent(str) || internal.IsDefOrHidden(str) { 91 return false 92 } 93 s.scope[str] = true 94 95 case *ast.Ident: 96 s.scope[x.Name] = true 97 98 case *ast.ListLit, *ast.Interpolation: 99 return false 100 } 101 return true 102} 103 104func (s *labelSimplifier) replace(c astutil.Cursor) bool { 105 switch x := c.Node().(type) { 106 case *ast.BasicLit: 107 str, err := strconv.Unquote(x.Value) 108 if err == nil && s.scope[str] && !internal.IsDefOrHidden(str) { 109 c.Replace(ast.NewIdent(str)) 110 } 111 } 112 return true 113} 114