github.com/rstandt/terraform@v0.12.32-0.20230710220336-b1063613405c/configs/compat_shim.go (about) 1 package configs 2 3 import ( 4 "github.com/hashicorp/hcl/v2" 5 "github.com/hashicorp/hcl/v2/hclsyntax" 6 "github.com/zclconf/go-cty/cty" 7 ) 8 9 // ------------------------------------------------------------------------- 10 // Functions in this file are compatibility shims intended to ease conversion 11 // from the old configuration loader. Any use of these functions that makes 12 // a change should generate a deprecation warning explaining to the user how 13 // to update their code for new patterns. 14 // 15 // Shims are particularly important for any patterns that have been widely 16 // documented in books, tutorials, etc. Users will still be starting from 17 // these examples and we want to help them adopt the latest patterns rather 18 // than leave them stranded. 19 // ------------------------------------------------------------------------- 20 21 // shimTraversalInString takes any arbitrary expression and checks if it is 22 // a quoted string in the native syntax. If it _is_, then it is parsed as a 23 // traversal and re-wrapped into a synthetic traversal expression and a 24 // warning is generated. Otherwise, the given expression is just returned 25 // verbatim. 26 // 27 // This function has no effect on expressions from the JSON syntax, since 28 // traversals in strings are the required pattern in that syntax. 29 // 30 // If wantKeyword is set, the generated warning diagnostic will talk about 31 // keywords rather than references. The behavior is otherwise unchanged, and 32 // the caller remains responsible for checking that the result is indeed 33 // a keyword, e.g. using hcl.ExprAsKeyword. 34 func shimTraversalInString(expr hcl.Expression, wantKeyword bool) (hcl.Expression, hcl.Diagnostics) { 35 // ObjectConsKeyExpr is a special wrapper type used for keys on object 36 // constructors to deal with the fact that naked identifiers are normally 37 // handled as "bareword" strings rather than as variable references. Since 38 // we know we're interpreting as a traversal anyway (and thus it won't 39 // matter whether it's a string or an identifier) we can safely just unwrap 40 // here and then process whatever we find inside as normal. 41 if ocke, ok := expr.(*hclsyntax.ObjectConsKeyExpr); ok { 42 expr = ocke.Wrapped 43 } 44 45 if !exprIsNativeQuotedString(expr) { 46 return expr, nil 47 } 48 49 strVal, diags := expr.Value(nil) 50 if diags.HasErrors() || strVal.IsNull() || !strVal.IsKnown() { 51 // Since we're not even able to attempt a shim here, we'll discard 52 // the diagnostics we saw so far and let the caller's own error 53 // handling take care of reporting the invalid expression. 54 return expr, nil 55 } 56 57 // The position handling here isn't _quite_ right because it won't 58 // take into account any escape sequences in the literal string, but 59 // it should be close enough for any error reporting to make sense. 60 srcRange := expr.Range() 61 startPos := srcRange.Start // copy 62 startPos.Column++ // skip initial quote 63 startPos.Byte++ // skip initial quote 64 65 traversal, tDiags := hclsyntax.ParseTraversalAbs( 66 []byte(strVal.AsString()), 67 srcRange.Filename, 68 startPos, 69 ) 70 diags = append(diags, tDiags...) 71 72 if wantKeyword { 73 diags = append(diags, &hcl.Diagnostic{ 74 Severity: hcl.DiagWarning, 75 Summary: "Quoted keywords are deprecated", 76 Detail: "In this context, keywords are expected literally rather than in quotes. Terraform 0.11 and earlier required quotes, but quoted keywords are now deprecated and will be removed in a future version of Terraform. Remove the quotes surrounding this keyword to silence this warning.", 77 Subject: &srcRange, 78 }) 79 } else { 80 diags = append(diags, &hcl.Diagnostic{ 81 Severity: hcl.DiagWarning, 82 Summary: "Quoted references are deprecated", 83 Detail: "In this context, references are expected literally rather than in quotes. Terraform 0.11 and earlier required quotes, but quoted references are now deprecated and will be removed in a future version of Terraform. Remove the quotes surrounding this reference to silence this warning.", 84 Subject: &srcRange, 85 }) 86 } 87 88 return &hclsyntax.ScopeTraversalExpr{ 89 Traversal: traversal, 90 SrcRange: srcRange, 91 }, diags 92 } 93 94 // shimIsIgnoreChangesStar returns true if the given expression seems to be 95 // a string literal whose value is "*". This is used to support a legacy 96 // form of ignore_changes = all . 97 // 98 // This function does not itself emit any diagnostics, so it's the caller's 99 // responsibility to emit a warning diagnostic when this function returns true. 100 func shimIsIgnoreChangesStar(expr hcl.Expression) bool { 101 val, valDiags := expr.Value(nil) 102 if valDiags.HasErrors() { 103 return false 104 } 105 if val.Type() != cty.String || val.IsNull() || !val.IsKnown() { 106 return false 107 } 108 return val.AsString() == "*" 109 } 110 111 // warnForDeprecatedInterpolations returns warning diagnostics if the given 112 // body can be proven to contain attributes whose expressions are native 113 // syntax expressions consisting entirely of a single template interpolation, 114 // which is a deprecated way to include a non-literal value in configuration. 115 // 116 // This is a best-effort sort of thing which relies on the physical HCL native 117 // syntax AST, so it might not catch everything. The main goal is to catch the 118 // "obvious" cases in order to help spread awareness that this old form is 119 // deprecated, when folks copy it from older examples they've found on the 120 // internet that were written for Terraform 0.11 or earlier. 121 func warnForDeprecatedInterpolationsInBody(body hcl.Body) hcl.Diagnostics { 122 var diags hcl.Diagnostics 123 124 nativeBody, ok := body.(*hclsyntax.Body) 125 if !ok { 126 // If it's not native syntax then we've nothing to do here. 127 return diags 128 } 129 130 for _, attr := range nativeBody.Attributes { 131 moreDiags := warnForDeprecatedInterpolationsInExpr(attr.Expr) 132 diags = append(diags, moreDiags...) 133 } 134 135 for _, block := range nativeBody.Blocks { 136 // We'll also go hunting in nested blocks 137 moreDiags := warnForDeprecatedInterpolationsInBody(block.Body) 138 diags = append(diags, moreDiags...) 139 } 140 141 return diags 142 } 143 144 func warnForDeprecatedInterpolationsInExpr(expr hcl.Expression) hcl.Diagnostics { 145 var diags hcl.Diagnostics 146 147 if _, ok := expr.(*hclsyntax.TemplateWrapExpr); !ok { 148 // We're only interested in TemplateWrapExpr, because that's how 149 // the HCL native syntax parser represents the case of a template 150 // that consists entirely of a single interpolation expression, which 151 // is therefore subject to the special case of passing through the 152 // inner value without conversion to string. 153 return diags 154 } 155 156 diags = append(diags, &hcl.Diagnostic{ 157 Severity: hcl.DiagWarning, 158 Summary: "Interpolation-only expressions are deprecated", 159 Detail: "Terraform 0.11 and earlier required all non-constant expressions to be provided via interpolation syntax, but this pattern is now deprecated. To silence this warning, remove the \"${ sequence from the start and the }\" sequence from the end of this expression, leaving just the inner expression.\n\nTemplate interpolation syntax is still used to construct strings from expressions when the template includes multiple interpolation sequences or a mixture of literal strings and interpolations. This deprecation applies only to templates that consist entirely of a single interpolation sequence.", 160 Subject: expr.Range().Ptr(), 161 }) 162 163 return diags 164 }