pulumi/pkg/codegen/hcl2/model/expression.go

2637 lines
75 KiB
Go
Raw Permalink Normal View History

// Copyright 2016-2020, Pulumi Corporation.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package model
import (
"fmt"
"io"
"math/big"
"strconv"
"github.com/hashicorp/hcl/v2"
"github.com/hashicorp/hcl/v2/hclsyntax"
"github.com/pulumi/pulumi/pkg/v3/codegen/hcl2/syntax"
"github.com/pulumi/pulumi/sdk/v3/go/common/slice"
"github.com/pulumi/pulumi/sdk/v3/go/common/util/contract"
"github.com/zclconf/go-cty/cty"
"github.com/zclconf/go-cty/cty/convert"
)
// Expression represents a semantically-analyzed HCL2 expression.
type Expression interface {
printable
// SyntaxNode returns the hclsyntax.Node associated with the expression.
SyntaxNode() hclsyntax.Node
// NodeTokens returns the syntax.Tokens associated with the expression.
NodeTokens() syntax.NodeTokens
// SetLeadingTrivia sets the leading trivia associated with the expression.
SetLeadingTrivia(syntax.TriviaList)
// SetTrailingTrivia sets the trailing trivia associated with the expression.
SetTrailingTrivia(syntax.TriviaList)
// Type returns the type of the expression.
Type() Type
// Typecheck recomputes the type of the expression, optionally typechecking its operands first.
Typecheck(typecheckOperands bool) hcl.Diagnostics
// Evaluate evaluates the expression.
Evaluate(context *hcl.EvalContext) (cty.Value, hcl.Diagnostics)
isExpression()
}
func identToken(token syntax.Token, ident string) syntax.Token {
if string(token.Raw.Bytes) != ident {
token.Raw.Bytes = []byte(ident)
}
return token
}
func exprHasLeadingTrivia(parens syntax.Parentheses, first interface{}) bool {
if parens.Any() {
return true
}
switch first := first.(type) {
case Expression:
return first.HasLeadingTrivia()
case bool:
return first
default:
contract.Failf("unexpected value of type %T for first", first)
return false
}
}
func exprHasTrailingTrivia(parens syntax.Parentheses, last interface{}) bool {
if parens.Any() {
return true
}
switch last := last.(type) {
case Expression:
return last.HasTrailingTrivia()
case bool:
return last
default:
contract.Failf("unexpected value of type %T for last", last)
return false
}
}
func getExprLeadingTrivia(parens syntax.Parentheses, first interface{}) syntax.TriviaList {
if parens.Any() {
return parens.GetLeadingTrivia()
}
switch first := first.(type) {
case Expression:
return first.GetLeadingTrivia()
case syntax.Token:
return first.LeadingTrivia
}
return nil
}
func setExprLeadingTrivia(parens syntax.Parentheses, first interface{}, trivia syntax.TriviaList) {
if parens.Any() {
parens.SetLeadingTrivia(trivia)
return
}
switch first := first.(type) {
case Expression:
first.SetLeadingTrivia(trivia)
case *syntax.Token:
first.LeadingTrivia = trivia
}
}
func getExprTrailingTrivia(parens syntax.Parentheses, last interface{}) syntax.TriviaList {
if parens.Any() {
return parens.GetTrailingTrivia()
}
switch last := last.(type) {
case Expression:
return last.GetTrailingTrivia()
case syntax.Token:
return last.TrailingTrivia
}
return nil
}
func setExprTrailingTrivia(parens syntax.Parentheses, last interface{}, trivia syntax.TriviaList) {
if parens.Any() {
parens.SetTrailingTrivia(trivia)
return
}
switch last := last.(type) {
case Expression:
last.SetTrailingTrivia(trivia)
case *syntax.Token:
last.TrailingTrivia = trivia
}
}
type syntaxExpr struct {
hclsyntax.LiteralValueExpr
expr Expression
}
func (x syntaxExpr) Value(context *hcl.EvalContext) (cty.Value, hcl.Diagnostics) {
return x.expr.Evaluate(context)
}
type hclExpression struct {
x Expression
}
func HCLExpression(x Expression) hcl.Expression {
return hclExpression{x: x}
}
func (x hclExpression) Value(context *hcl.EvalContext) (cty.Value, hcl.Diagnostics) {
return x.x.Evaluate(context)
}
func (x hclExpression) Variables() []hcl.Traversal {
var variables []hcl.Traversal
scope := NewRootScope(syntax.None)
_, diags := VisitExpression(x.x, func(n Expression) (Expression, hcl.Diagnostics) {
switch n := n.(type) {
case *AnonymousFunctionExpression:
scope = scope.Push(syntax.None)
for _, p := range n.Parameters {
scope.Define(p.Name, p)
}
case *ForExpression:
scope = scope.Push(syntax.None)
if n.KeyVariable != nil {
scope.Define(n.KeyVariable.Name, n.KeyVariable)
}
scope.Define(n.ValueVariable.Name, n.ValueVariable)
}
return n, nil
}, func(n Expression) (Expression, hcl.Diagnostics) {
switch n := n.(type) {
case *AnonymousFunctionExpression, *ForExpression:
scope = scope.Pop()
case *ScopeTraversalExpression:
if _, isSplatVariable := n.Parts[0].(*SplatVariable); !isSplatVariable {
if _, defined := scope.BindReference(n.RootName); !defined {
variables = append(variables, n.Traversal.SimpleSplit().Abs)
}
}
}
return n, nil
})
contract.Assertf(len(diags) == 0, "unexpected diagnostics: %v", diags)
return variables
}
func (x hclExpression) Range() hcl.Range {
if syntax := x.x.SyntaxNode(); syntax != nil {
return syntax.Range()
}
return hcl.Range{}
}
func (x hclExpression) StartRange() hcl.Range {
if syntax := x.x.SyntaxNode(); syntax != nil {
return syntax.(hcl.Expression).StartRange()
}
return hcl.Range{}
}
func operatorPrecedence(op *hclsyntax.Operation) int {
switch op {
case hclsyntax.OpLogicalOr:
return 1
case hclsyntax.OpLogicalAnd:
return 2
case hclsyntax.OpEqual, hclsyntax.OpNotEqual:
return 3
case hclsyntax.OpGreaterThan, hclsyntax.OpGreaterThanOrEqual, hclsyntax.OpLessThan, hclsyntax.OpLessThanOrEqual:
return 4
case hclsyntax.OpAdd, hclsyntax.OpSubtract:
return 5
case hclsyntax.OpMultiply, hclsyntax.OpDivide, hclsyntax.OpModulo:
return 6
case hclsyntax.OpNegate, hclsyntax.OpLogicalNot:
return 7
default:
return 8
}
}
// AnonymousFunctionExpression represents a semantically-analyzed anonymous function expression.
//
// These expressions are not the result of semantically analyzing syntax nodes. Instead, they may be synthesized by
// transforms over the IR for a program (e.g. the Apply transform).
type AnonymousFunctionExpression struct {
// The signature for the anonymous function.
Signature StaticFunctionSignature
// The parameter definitions for the anonymous function.
Parameters []*Variable
// The body of the anonymous function.
Body Expression
}
// SyntaxNode returns the syntax node associated with the body of the anonymous function.
func (x *AnonymousFunctionExpression) SyntaxNode() hclsyntax.Node {
return x.Body.SyntaxNode()
}
// NodeTokens returns the tokens associated with the body of the anonymous function.
func (x *AnonymousFunctionExpression) NodeTokens() syntax.NodeTokens {
return x.Body.NodeTokens()
}
// Type returns the type of the anonymous function expression.
//
// TODO: currently this returns the any type. Instead, it should return a function type.
func (x *AnonymousFunctionExpression) Type() Type {
return DynamicType
}
func (x *AnonymousFunctionExpression) Typecheck(typecheckOperands bool) hcl.Diagnostics {
var diagnostics hcl.Diagnostics
if typecheckOperands {
bodyDiags := x.Body.Typecheck(true)
diagnostics = append(diagnostics, bodyDiags...)
}
return diagnostics
}
func (x *AnonymousFunctionExpression) Evaluate(context *hcl.EvalContext) (cty.Value, hcl.Diagnostics) {
return cty.NilVal, hcl.Diagnostics{cannotEvaluateAnonymousFunctionExpressions()}
}
func (x *AnonymousFunctionExpression) HasLeadingTrivia() bool {
return x.Body.HasLeadingTrivia()
}
func (x *AnonymousFunctionExpression) HasTrailingTrivia() bool {
return x.Body.HasTrailingTrivia()
}
func (x *AnonymousFunctionExpression) GetLeadingTrivia() syntax.TriviaList {
return x.Body.GetLeadingTrivia()
}
func (x *AnonymousFunctionExpression) SetLeadingTrivia(t syntax.TriviaList) {
x.Body.SetLeadingTrivia(t)
}
func (x *AnonymousFunctionExpression) GetTrailingTrivia() syntax.TriviaList {
return x.Body.GetTrailingTrivia()
}
func (x *AnonymousFunctionExpression) SetTrailingTrivia(t syntax.TriviaList) {
x.Body.SetTrailingTrivia(t)
}
func (x *AnonymousFunctionExpression) Format(f fmt.State, c rune) {
x.print(f, &printer{})
}
func (x *AnonymousFunctionExpression) print(w io.Writer, p *printer) {
// Print a call to eval.
p.fprintf(w, "eval(")
// Print the parameter names.
for _, v := range x.Parameters {
p.fprintf(w, "%v, ", v.Name)
}
// Print the body and closing paren.
p.fprintf(w, "%v)", x.Body)
}
func (*AnonymousFunctionExpression) isExpression() {}
// BinaryOpExpression represents a semantically-analyzed binary operation.
type BinaryOpExpression struct {
// The syntax node associated with the binary operation.
Syntax *hclsyntax.BinaryOpExpr
// The tokens associated with the expression, if any.
Tokens *syntax.BinaryOpTokens
// The left-hand operand of the operation.
LeftOperand Expression
// The operation.
Operation *hclsyntax.Operation
// The right-hand operand of the operation.
RightOperand Expression
leftType Type
rightType Type
exprType Type
}
// SyntaxNode returns the syntax node associated with the binary operation.
func (x *BinaryOpExpression) SyntaxNode() hclsyntax.Node {
if x.Syntax == nil {
return syntax.None
}
return x.Syntax
}
// NodeTokens returns the tokens associated with the binary operation.
func (x *BinaryOpExpression) NodeTokens() syntax.NodeTokens {
return x.Tokens
}
// LeftOperandType returns the desired type for the left operand of the binary operation.
func (x *BinaryOpExpression) LeftOperandType() Type {
return x.leftType
}
// RightOperandType returns the desired type for the right operand of the binary operation.
func (x *BinaryOpExpression) RightOperandType() Type {
return x.rightType
}
// Type returns the type of the binary operation.
func (x *BinaryOpExpression) Type() Type {
return x.exprType
}
func (x *BinaryOpExpression) Typecheck(typecheckOperands bool) hcl.Diagnostics {
var diagnostics hcl.Diagnostics
var rng hcl.Range
if x.Syntax != nil {
rng = x.Syntax.Range()
}
if typecheckOperands {
leftDiags := x.LeftOperand.Typecheck(true)
diagnostics = append(diagnostics, leftDiags...)
rightDiags := x.RightOperand.Typecheck(true)
diagnostics = append(diagnostics, rightDiags...)
}
// Compute the signature for the operator and typecheck the arguments.
signature := getOperationSignature(x.Operation)
contract.Assertf(len(signature.Parameters) == 2,
"expected binary operator signature to have two parameters, got %v", len(signature.Parameters))
x.leftType = signature.Parameters[0].Type
x.rightType = signature.Parameters[1].Type
typecheckDiags := typecheckArgs(rng, signature, x.LeftOperand, x.RightOperand)
diagnostics = append(diagnostics, typecheckDiags...)
x.exprType = liftOperationType(signature.ReturnType, x.LeftOperand, x.RightOperand)
return diagnostics
}
func (x *BinaryOpExpression) Evaluate(context *hcl.EvalContext) (cty.Value, hcl.Diagnostics) {
syntax := &hclsyntax.BinaryOpExpr{
LHS: &syntaxExpr{expr: x.LeftOperand},
Op: x.Operation,
RHS: &syntaxExpr{expr: x.RightOperand},
}
return syntax.Value(context)
}
func (x *BinaryOpExpression) HasLeadingTrivia() bool {
return exprHasLeadingTrivia(x.Tokens.GetParentheses(), x.LeftOperand)
}
func (x *BinaryOpExpression) HasTrailingTrivia() bool {
return exprHasTrailingTrivia(x.Tokens.GetParentheses(), x.RightOperand)
}
func (x *BinaryOpExpression) GetLeadingTrivia() syntax.TriviaList {
return getExprLeadingTrivia(x.Tokens.GetParentheses(), x.LeftOperand)
}
func (x *BinaryOpExpression) SetLeadingTrivia(t syntax.TriviaList) {
setExprLeadingTrivia(x.Tokens.GetParentheses(), x.LeftOperand, t)
}
func (x *BinaryOpExpression) GetTrailingTrivia() syntax.TriviaList {
return getExprTrailingTrivia(x.Tokens.GetParentheses(), x.RightOperand)
}
func (x *BinaryOpExpression) SetTrailingTrivia(t syntax.TriviaList) {
if x.Tokens == nil {
x.Tokens = syntax.NewBinaryOpTokens(x.Operation)
}
setExprTrailingTrivia(x.Tokens.GetParentheses(), x.RightOperand, t)
}
func (x *BinaryOpExpression) Format(f fmt.State, c rune) {
x.print(f, &printer{})
}
func (x *BinaryOpExpression) print(w io.Writer, p *printer) {
precedence := operatorPrecedence(x.Operation)
p.fprintf(w, "%[2](%.[1]*[3]v% [4]v% .[1]*[5]o%[6])",
precedence,
x.Tokens.GetParentheses(),
x.LeftOperand, x.Tokens.GetOperator(x.Operation), x.RightOperand,
x.Tokens.GetParentheses())
}
func (*BinaryOpExpression) isExpression() {}
// ConditionalExpression represents a semantically-analzed conditional expression (i.e.
// <condition> '?' <true> ':' <false>).
type ConditionalExpression struct {
// The syntax node associated with the conditional expression.
Syntax *hclsyntax.ConditionalExpr
// The tokens associated with the expression, if any.
Tokens syntax.NodeTokens
// The condition.
Condition Expression
// The result of the expression if the condition evaluates to true.
TrueResult Expression
// The result of the expression if the condition evaluates to false.
FalseResult Expression
exprType Type
}
// SyntaxNode returns the syntax node associated with the conditional expression.
func (x *ConditionalExpression) SyntaxNode() hclsyntax.Node {
if x.Syntax == nil {
return syntax.None
}
return x.Syntax
}
// NodeTokens returns the tokens associated with the conditional expression.
func (x *ConditionalExpression) NodeTokens() syntax.NodeTokens {
return x.Tokens
}
// Type returns the type of the conditional expression.
func (x *ConditionalExpression) Type() Type {
return x.exprType
}
func (x *ConditionalExpression) Typecheck(typecheckOperands bool) hcl.Diagnostics {
var diagnostics hcl.Diagnostics
if typecheckOperands {
conditionDiags := x.Condition.Typecheck(true)
diagnostics = append(diagnostics, conditionDiags...)
trueDiags := x.TrueResult.Typecheck(true)
diagnostics = append(diagnostics, trueDiags...)
falseDiags := x.FalseResult.Typecheck(true)
diagnostics = append(diagnostics, falseDiags...)
}
// Compute the type of the result.
resultType, _ := UnifyTypes(x.TrueResult.Type(), x.FalseResult.Type())
// Typecheck the condition expression.
if InputType(BoolType).ConversionFrom(x.Condition.Type()) == NoConversion {
diagnostics = append(diagnostics, ExprNotConvertible(InputType(BoolType), x.Condition))
}
x.exprType = liftOperationType(resultType, x.Condition)
return diagnostics
}
func (x *ConditionalExpression) Evaluate(context *hcl.EvalContext) (cty.Value, hcl.Diagnostics) {
syntax := &hclsyntax.ConditionalExpr{
Condition: &syntaxExpr{expr: x.Condition},
TrueResult: &syntaxExpr{expr: x.TrueResult},
FalseResult: &syntaxExpr{expr: x.FalseResult},
}
return syntax.Value(context)
}
func (x *ConditionalExpression) HasLeadingTrivia() bool {
switch tokens := x.Tokens.(type) {
case *syntax.ConditionalTokens:
if tokens.Parentheses.Any() {
return true
}
case *syntax.TemplateConditionalTokens:
return len(tokens.OpenIf.LeadingTrivia) != 0
}
return x.Condition.HasLeadingTrivia()
}
func (x *ConditionalExpression) HasTrailingTrivia() bool {
switch tokens := x.Tokens.(type) {
case *syntax.ConditionalTokens:
if tokens.Parentheses.Any() {
return true
}
case *syntax.TemplateConditionalTokens:
return len(tokens.CloseEndif.TrailingTrivia) != 0
}
return x.FalseResult.HasTrailingTrivia()
}
func (x *ConditionalExpression) GetLeadingTrivia() syntax.TriviaList {
switch tokens := x.Tokens.(type) {
case *syntax.ConditionalTokens:
if tokens.Parentheses.Any() {
return tokens.Parentheses.GetLeadingTrivia()
}
case *syntax.TemplateConditionalTokens:
return tokens.OpenIf.LeadingTrivia
}
return x.Condition.GetLeadingTrivia()
}
func (x *ConditionalExpression) SetLeadingTrivia(t syntax.TriviaList) {
switch tokens := x.Tokens.(type) {
case *syntax.ConditionalTokens:
if tokens.Parentheses.Any() {
tokens.Parentheses.SetLeadingTrivia(t)
return
}
case *syntax.TemplateConditionalTokens:
tokens.OpenIf.LeadingTrivia = t
return
}
x.Condition.SetLeadingTrivia(t)
}
func (x *ConditionalExpression) GetTrailingTrivia() syntax.TriviaList {
switch tokens := x.Tokens.(type) {
case *syntax.ConditionalTokens:
if tokens.Parentheses.Any() {
return tokens.Parentheses.GetTrailingTrivia()
}
case *syntax.TemplateConditionalTokens:
return tokens.CloseEndif.TrailingTrivia
}
return x.FalseResult.GetTrailingTrivia()
}
func (x *ConditionalExpression) SetTrailingTrivia(t syntax.TriviaList) {
if x.Tokens == nil {
x.Tokens = syntax.NewConditionalTokens()
}
switch tokens := x.Tokens.(type) {
case *syntax.ConditionalTokens:
if tokens.Parentheses.Any() {
tokens.Parentheses.SetTrailingTrivia(t)
return
}
case *syntax.TemplateConditionalTokens:
tokens.CloseEndif.TrailingTrivia = t
return
}
x.FalseResult.SetTrailingTrivia(t)
}
func (x *ConditionalExpression) Format(f fmt.State, c rune) {
x.print(f, &printer{})
}
func (x *ConditionalExpression) print(w io.Writer, p *printer) {
tokens := x.Tokens
if tokens == nil {
tokens = syntax.NewConditionalTokens()
}
switch tokens := tokens.(type) {
case *syntax.ConditionalTokens:
p.fprintf(w, "%(%v% v% v% v% v%)",
tokens.Parentheses,
x.Condition, tokens.QuestionMark, x.TrueResult, tokens.Colon, x.FalseResult,
tokens.Parentheses)
case *syntax.TemplateConditionalTokens:
p.fprintf(w, "%v%v% v%v%v", tokens.OpenIf, tokens.If, x.Condition, tokens.CloseIf, x.TrueResult)
if tokens.Else != nil {
p.fprintf(w, "%v%v%v%v", tokens.OpenElse, tokens.Else, tokens.CloseElse, x.FalseResult)
}
p.fprintf(w, "%v%v%v", tokens.OpenEndif, tokens.Endif, tokens.CloseEndif)
}
}
func (*ConditionalExpression) isExpression() {}
// ErrorExpression represents an expression that could not be bound due to an error.
type ErrorExpression struct {
// The syntax node associated with the error, if any.
Syntax hclsyntax.Node
// The tokens associated with the error.
Tokens syntax.NodeTokens
// The message associated with the error.
Message string
exprType Type
}
// SyntaxNode returns the syntax node associated with the error expression.
func (x *ErrorExpression) SyntaxNode() hclsyntax.Node {
return x.Syntax
}
// NodeTokens returns the tokens associated with the error expression.
func (x *ErrorExpression) NodeTokens() syntax.NodeTokens {
return x.Tokens
}
// Type returns the type of the error expression.
func (x *ErrorExpression) Type() Type {
return x.exprType
}
func (x *ErrorExpression) Typecheck(typecheckOperands bool) hcl.Diagnostics {
return nil
}
func (x *ErrorExpression) Evaluate(context *hcl.EvalContext) (cty.Value, hcl.Diagnostics) {
return cty.DynamicVal, hcl.Diagnostics{{
Severity: hcl.DiagError,
Summary: x.Message,
}}
}
func (x *ErrorExpression) HasLeadingTrivia() bool {
return false
}
func (x *ErrorExpression) HasTrailingTrivia() bool {
return false
}
func (x *ErrorExpression) GetLeadingTrivia() syntax.TriviaList {
return nil
}
func (x *ErrorExpression) SetLeadingTrivia(t syntax.TriviaList) {
}
func (x *ErrorExpression) GetTrailingTrivia() syntax.TriviaList {
return nil
}
func (x *ErrorExpression) SetTrailingTrivia(t syntax.TriviaList) {
}
func (x *ErrorExpression) Format(f fmt.State, c rune) {
x.print(f, &printer{})
}
func (x *ErrorExpression) print(w io.Writer, p *printer) {
p.fprintf(w, "error(%q)", x.Message)
}
func (*ErrorExpression) isExpression() {}
// ForExpression represents a semantically-analyzed for expression.
type ForExpression struct {
fmt.Formatter
// The syntax node associated with the for expression.
Syntax *hclsyntax.ForExpr
// The tokens associated with the expression, if any.
Tokens syntax.NodeTokens
// The key variable, if any.
KeyVariable *Variable
// The value variable.
ValueVariable *Variable
// The collection being iterated.
Collection Expression
// The expression that generates the keys of the result, if any. If this field is non-nil, the result is a map.
Key Expression
// The expression that generates the values of the result.
Value Expression
// The condition that filters the items of the result, if any.
Condition Expression
// True if the value expression is being grouped.
Group bool
// Whether the collection type should be strictly type-checked
// When true, unsupported collection types will result in an error
// otherwise a warning will be emitted
StrictCollectionTypechecking bool
exprType Type
}
// SyntaxNode returns the syntax node associated with the for expression.
func (x *ForExpression) SyntaxNode() hclsyntax.Node {
if x.Syntax == nil {
return syntax.None
}
return x.Syntax
}
// NodeTokens returns the tokens associated with the for expression.
func (x *ForExpression) NodeTokens() syntax.NodeTokens {
return x.Tokens
}
// Type returns the type of the for expression.
func (x *ForExpression) Type() Type {
return x.exprType
}
func (x *ForExpression) typecheck(typecheckCollection, typecheckOperands bool) hcl.Diagnostics {
var diagnostics hcl.Diagnostics
var rng hcl.Range
if x.Syntax != nil {
rng = x.Syntax.CollExpr.Range()
}
if typecheckOperands {
collectionDiags := x.Collection.Typecheck(true)
diagnostics = append(diagnostics, collectionDiags...)
}
if typecheckCollection {
keyType, valueType, kvDiags := GetCollectionTypes(x.Collection.Type(), rng, x.StrictCollectionTypechecking)
diagnostics = append(diagnostics, kvDiags...)
if x.KeyVariable != nil {
x.KeyVariable.VariableType = keyType
}
x.ValueVariable.VariableType = valueType
}
if typecheckOperands {
if x.Key != nil {
keyDiags := x.Key.Typecheck(true)
diagnostics = append(diagnostics, keyDiags...)
}
valueDiags := x.Value.Typecheck(true)
diagnostics = append(diagnostics, valueDiags...)
if x.Condition != nil {
conditionDiags := x.Condition.Typecheck(true)
diagnostics = append(diagnostics, conditionDiags...)
}
}
if x.Key != nil {
// A key expression is only present when producing a map. Key types must therefore be strings.
if !InputType(StringType).ConversionFrom(x.Key.Type()).Exists() {
diagnostics = append(diagnostics, ExprNotConvertible(InputType(StringType), x.Key))
}
}
if x.Condition != nil {
if !InputType(BoolType).ConversionFrom(x.Condition.Type()).Exists() {
diagnostics = append(diagnostics, ExprNotConvertible(InputType(BoolType), x.Condition))
}
}
// If there is a key expression, we are producing a map. Otherwise, we are producing an list. In either case, wrap
// the result type in the same set of eventuals and optionals present in the collection type.
var resultType Type
if x.Key != nil {
valueType := x.Value.Type()
if x.Group {
valueType = NewListType(valueType)
}
resultType = wrapIterableResultType(x.Collection.Type(), NewMapType(valueType))
} else {
resultType = wrapIterableResultType(x.Collection.Type(), NewListType(x.Value.Type()))
}
// If either the key expression or the condition expression is eventual, the result is eventual: each of these
// values is required to determine which items are present in the result.
var liftArgs []Expression
if x.Key != nil {
liftArgs = append(liftArgs, x.Key)
}
if x.Condition != nil {
liftArgs = append(liftArgs, x.Condition)
}
x.exprType = liftOperationType(resultType, liftArgs...)
return diagnostics
}
func (x *ForExpression) Typecheck(typecheckOperands bool) hcl.Diagnostics {
return x.typecheck(true, typecheckOperands)
}
func (x *ForExpression) Evaluate(context *hcl.EvalContext) (cty.Value, hcl.Diagnostics) {
syntax := &hclsyntax.ForExpr{
ValVar: x.ValueVariable.Name,
CollExpr: &syntaxExpr{expr: x.Collection},
ValExpr: &syntaxExpr{expr: x.Value},
Group: x.Group,
}
if x.KeyVariable != nil {
syntax.KeyVar = x.KeyVariable.Name
}
if x.Key != nil {
syntax.KeyExpr = &syntaxExpr{expr: x.Key}
}
if x.Condition != nil {
syntax.CondExpr = &syntaxExpr{expr: x.Condition}
}
return syntax.Value(context)
}
func (x *ForExpression) HasLeadingTrivia() bool {
return x.Tokens != nil
}
func (x *ForExpression) HasTrailingTrivia() bool {
return x.Tokens != nil
}
func (x *ForExpression) GetLeadingTrivia() syntax.TriviaList {
switch tokens := x.Tokens.(type) {
case *syntax.ForTokens:
return getExprLeadingTrivia(tokens.Parentheses, tokens.Open)
case *syntax.TemplateForTokens:
return tokens.OpenFor.LeadingTrivia
default:
return nil
}
}
func (x *ForExpression) SetLeadingTrivia(t syntax.TriviaList) {
if x.Tokens == nil {
keyVariable := ""
if x.KeyVariable != nil {
keyVariable = x.KeyVariable.Name
}
x.Tokens = syntax.NewForTokens(keyVariable, x.ValueVariable.Name, x.Key != nil, x.Group, x.Condition != nil)
}
switch tokens := x.Tokens.(type) {
case *syntax.ForTokens:
setExprLeadingTrivia(tokens.Parentheses, &tokens.Open, t)
case *syntax.TemplateForTokens:
tokens.OpenFor.LeadingTrivia = t
}
}
func (x *ForExpression) GetTrailingTrivia() syntax.TriviaList {
switch tokens := x.Tokens.(type) {
case *syntax.ForTokens:
return getExprTrailingTrivia(tokens.Parentheses, tokens.Close)
case *syntax.TemplateForTokens:
return tokens.CloseEndfor.TrailingTrivia
default:
return nil
}
}
func (x *ForExpression) SetTrailingTrivia(t syntax.TriviaList) {
if x.Tokens == nil {
keyVariable := ""
if x.KeyVariable != nil {
keyVariable = x.KeyVariable.Name
}
x.Tokens = syntax.NewForTokens(keyVariable, x.ValueVariable.Name, x.Key != nil, x.Group, x.Condition != nil)
}
switch tokens := x.Tokens.(type) {
case *syntax.ForTokens:
setExprTrailingTrivia(tokens.Parentheses, &tokens.Close, t)
case *syntax.TemplateForTokens:
tokens.CloseEndfor.TrailingTrivia = t
}
}
func (x *ForExpression) Format(f fmt.State, c rune) {
x.print(f, &printer{})
}
func (x *ForExpression) print(w io.Writer, p *printer) {
tokens := x.Tokens
if tokens == nil {
keyVariable := ""
if x.KeyVariable != nil {
keyVariable = x.KeyVariable.Name
}
syntax.NewForTokens(keyVariable, x.ValueVariable.Name, x.Key != nil, x.Group, x.Condition != nil)
}
switch tokens := tokens.(type) {
case *syntax.ForTokens:
// Print the opening rune and the for token.
p.fprintf(w, "%(%v%v", tokens.Parentheses, tokens.Open, tokens.For)
// Print the key variable, if any.
if x.KeyVariable != nil {
keyToken := tokens.Key
if x.KeyVariable != nil && keyToken == nil {
keyToken = &syntax.Token{Raw: hclsyntax.Token{Type: hclsyntax.TokenIdent}}
}
key := identToken(*keyToken, x.KeyVariable.Name)
p.fprintf(w, "% v%v", key, tokens.Comma)
}
// Print the value variable, the in token, the collection expression, and the colon.
value := identToken(tokens.Value, x.ValueVariable.Name)
p.fprintf(w, "% v% v% v%v", value, tokens.In, x.Collection, tokens.Colon)
// Print the key expression and arrow token, if any.
if x.Key != nil {
p.fprintf(w, "% v% v", x.Key, tokens.Arrow)
}
// Print the value expression.
p.fprintf(w, "% v", x.Value)
// Print the group token, if any.
if x.Group {
p.fprintf(w, "%v", tokens.Group)
}
// Print the if token and the condition, if any.
if x.Condition != nil {
p.fprintf(w, "% v% v", tokens.If, x.Condition)
}
// Print the closing rune.
p.fprintf(w, "%v%)", tokens.Close, tokens.Parentheses)
case *syntax.TemplateForTokens:
// Print the opening sequence.
p.fprintf(w, "%v%v", tokens.OpenFor, tokens.For)
// Print the key variable, if any.
if x.KeyVariable != nil {
keyToken := tokens.Key
if x.KeyVariable != nil && keyToken == nil {
keyToken = &syntax.Token{Raw: hclsyntax.Token{Type: hclsyntax.TokenIdent}}
}
key := identToken(*keyToken, x.KeyVariable.Name)
p.fprintf(w, "% v%v", key, tokens.Comma)
}
// Print the value variable, the in token, the collection expression, the control sequence terminator, the
// value expression, and the closing sequence.
p.fprintf(w, "% v% v% v%v%v%v%v%v",
identToken(tokens.Value, x.ValueVariable.Name), tokens.In, x.Collection, tokens.CloseFor,
x.Value,
tokens.OpenEndfor, tokens.Endfor, tokens.CloseEndfor)
}
}
func (*ForExpression) isExpression() {}
// FunctionCallExpression represents a semantically-analyzed function call expression.
type FunctionCallExpression struct {
// The syntax node associated with the function call expression.
Syntax *hclsyntax.FunctionCallExpr
// The tokens associated with the expression, if any.
Tokens *syntax.FunctionCallTokens
// The name of the called function.
Name string
// The signature of the called function.
Signature StaticFunctionSignature
// The arguments to the function call.
Args []Expression
// ExpandFinal indicates that the final argument should be a tuple, list, or set whose elements will be passed as
// individual arguments to the function.
ExpandFinal bool
}
// SyntaxNode returns the syntax node associated with the function call expression.
func (x *FunctionCallExpression) SyntaxNode() hclsyntax.Node {
if x.Syntax == nil {
return syntax.None
}
return x.Syntax
}
// NodeTokens returns the tokens associated with the function call expression.
func (x *FunctionCallExpression) NodeTokens() syntax.NodeTokens {
return x.Tokens
}
// Type returns the type of the function call expression.
func (x *FunctionCallExpression) Type() Type {
return x.Signature.ReturnType
}
func (x *FunctionCallExpression) Typecheck(typecheckOperands bool) hcl.Diagnostics {
var diagnostics hcl.Diagnostics
if typecheckOperands {
for _, arg := range x.Args {
argDiagnostics := arg.Typecheck(true)
diagnostics = append(diagnostics, argDiagnostics...)
}
}
var rng hcl.Range
if x.Syntax != nil {
rng = x.Syntax.Range()
}
// Typecheck the function's arguments.
typecheckDiags := typecheckArgs(rng, x.Signature, x.Args...)
diagnostics = append(diagnostics, typecheckDiags...)
// Unless the function is already automatically using an
// Output-returning version, modify the signature to account
// for automatic lifting to Promise or Output.
_, isOutput := x.Signature.ReturnType.(*OutputType)
if !isOutput {
x.Signature.ReturnType = liftOperationType(x.Signature.ReturnType, x.Args...)
}
return diagnostics
}
func (x *FunctionCallExpression) Evaluate(context *hcl.EvalContext) (cty.Value, hcl.Diagnostics) {
syntax := &hclsyntax.FunctionCallExpr{
Name: x.Name,
Args: make([]hclsyntax.Expression, len(x.Args)),
ExpandFinal: x.ExpandFinal,
}
for i, arg := range x.Args {
syntax.Args[i] = &syntaxExpr{expr: arg}
}
return syntax.Value(context)
}
func (x *FunctionCallExpression) HasLeadingTrivia() bool {
return exprHasLeadingTrivia(x.Tokens.GetParentheses(), x.Tokens != nil)
}
func (x *FunctionCallExpression) HasTrailingTrivia() bool {
return exprHasTrailingTrivia(x.Tokens.GetParentheses(), x.Tokens != nil)
}
func (x *FunctionCallExpression) GetLeadingTrivia() syntax.TriviaList {
return getExprLeadingTrivia(x.Tokens.GetParentheses(), x.Tokens.GetName(x.Name))
}
func (x *FunctionCallExpression) SetLeadingTrivia(t syntax.TriviaList) {
if x.Tokens == nil {
x.Tokens = syntax.NewFunctionCallTokens(x.Name, len(x.Args))
}
setExprLeadingTrivia(x.Tokens.Parentheses, &x.Tokens.Name, t)
}
func (x *FunctionCallExpression) GetTrailingTrivia() syntax.TriviaList {
return getExprTrailingTrivia(x.Tokens.GetParentheses(), x.Tokens.GetCloseParen())
}
func (x *FunctionCallExpression) SetTrailingTrivia(t syntax.TriviaList) {
if x.Tokens == nil {
x.Tokens = syntax.NewFunctionCallTokens(x.Name, len(x.Args))
}
setExprTrailingTrivia(x.Tokens.Parentheses, &x.Tokens.CloseParen, t)
}
func (x *FunctionCallExpression) Format(f fmt.State, c rune) {
x.print(f, &printer{})
}
func (x *FunctionCallExpression) print(w io.Writer, p *printer) {
// Print the name and opening parenthesis.
p.fprintf(w, "%(%v%v", x.Tokens.GetParentheses(), x.Tokens.GetName(x.Name), x.Tokens.GetOpenParen())
// Print each argument and its comma.
commas := x.Tokens.GetCommas(len(x.Args))
for i, arg := range x.Args {
if i == 0 {
p.fprintf(w, "%v", arg)
} else {
p.fprintf(w, "% v", arg)
}
if i < len(x.Args)-1 {
var comma syntax.Token
if i < len(commas) {
comma = commas[i]
}
p.fprintf(w, "%v", comma)
}
}
// If there were commas left over, print the trivia for each.
if len(x.Args) > 0 && len(x.Args)-1 <= len(commas) {
for _, comma := range commas[len(x.Args)-1:] {
p.fprintf(w, "%v", comma.AllTrivia().CollapseWhitespace())
}
}
// Print the closing parenthesis.
p.fprintf(w, "%v%)", x.Tokens.GetCloseParen(), x.Tokens.GetParentheses())
}
func (*FunctionCallExpression) isExpression() {}
// IndexExpression represents a semantically-analyzed index expression.
type IndexExpression struct {
// The syntax node associated with the index expression.
Syntax *hclsyntax.IndexExpr
// The tokens associated with the expression, if any.
Tokens *syntax.IndexTokens
// The collection being indexed.
Collection Expression
// The index key.
Key Expression
// Whether the collection type should be strictly type-checked
// When true, unsupported collection types will result in an error
// otherwise a warning will be emitted
StrictCollectionTypechecking bool
keyType Type
exprType Type
}
// SyntaxNode returns the syntax node associated with the index expression.
func (x *IndexExpression) SyntaxNode() hclsyntax.Node {
if x.Syntax == nil {
return syntax.None
}
return x.Syntax
}
// NodeTokens returns the tokens associated with the index expression.
func (x *IndexExpression) NodeTokens() syntax.NodeTokens {
return x.Tokens
}
// KeyType returns the expected type of the index expression's key.
func (x *IndexExpression) KeyType() Type {
return x.keyType
}
// Type returns the type of the index expression.
func (x *IndexExpression) Type() Type {
return x.exprType
}
func (x *IndexExpression) Typecheck(typecheckOperands bool) hcl.Diagnostics {
var diagnostics hcl.Diagnostics
if typecheckOperands {
collectionDiags := x.Collection.Typecheck(true)
diagnostics = append(diagnostics, collectionDiags...)
keyDiags := x.Key.Typecheck(true)
diagnostics = append(diagnostics, keyDiags...)
}
var rng hcl.Range
if x.Syntax != nil {
rng = x.Syntax.Collection.Range()
}
keyType, valueType, kvDiags := GetCollectionTypes(x.Collection.Type(), rng, x.StrictCollectionTypechecking)
diagnostics = append(diagnostics, kvDiags...)
x.keyType = keyType
if lit, ok := x.Key.(*LiteralValueExpression); ok {
traverser := hcl.TraverseIndex{
Key: lit.Value,
}
valueType, traverseDiags := x.Collection.Type().Traverse(traverser)
if len(traverseDiags) == 0 {
x.exprType = valueType.(Type)
return diagnostics
}
}
if !InputType(keyType).ConversionFrom(x.Key.Type()).Exists() {
diagnostics = append(diagnostics, ExprNotConvertible(InputType(keyType), x.Key))
}
resultType := wrapIterableResultType(x.Collection.Type(), valueType)
x.exprType = liftOperationType(resultType, x.Key)
return diagnostics
}
func (x *IndexExpression) Evaluate(context *hcl.EvalContext) (cty.Value, hcl.Diagnostics) {
syntax := &hclsyntax.IndexExpr{
Collection: &syntaxExpr{expr: x.Collection},
Key: &syntaxExpr{expr: x.Key},
}
return syntax.Value(context)
}
func (x *IndexExpression) HasLeadingTrivia() bool {
return exprHasLeadingTrivia(x.Tokens.GetParentheses(), x.Collection)
}
func (x *IndexExpression) HasTrailingTrivia() bool {
return exprHasTrailingTrivia(x.Tokens.GetParentheses(), x.Tokens != nil)
}
func (x *IndexExpression) GetLeadingTrivia() syntax.TriviaList {
return getExprLeadingTrivia(x.Tokens.GetParentheses(), x.Tokens.GetOpenBracket())
}
func (x *IndexExpression) SetLeadingTrivia(t syntax.TriviaList) {
if x.Tokens == nil {
x.Tokens = syntax.NewIndexTokens()
}
setExprLeadingTrivia(x.Tokens.Parentheses, x.Collection, t)
}
func (x *IndexExpression) GetTrailingTrivia() syntax.TriviaList {
return getExprTrailingTrivia(x.Tokens.GetParentheses(), x.Tokens.GetCloseBracket())
}
func (x *IndexExpression) SetTrailingTrivia(t syntax.TriviaList) {
if x.Tokens == nil {
x.Tokens = syntax.NewIndexTokens()
}
setExprTrailingTrivia(x.Tokens.Parentheses, &x.Tokens.CloseBracket, t)
}
func (x *IndexExpression) Format(f fmt.State, c rune) {
x.print(f, &printer{})
}
func (x *IndexExpression) print(w io.Writer, p *printer) {
p.fprintf(w, "%(%v%v%v%v%)",
x.Tokens.GetParentheses(),
x.Collection, x.Tokens.GetOpenBracket(), x.Key, x.Tokens.GetCloseBracket(),
x.Tokens.GetParentheses())
}
func (*IndexExpression) isExpression() {}
func literalText(value cty.Value, rawBytes []byte, escaped, quoted bool) string {
if len(rawBytes) > 0 {
parsed, diags := hclsyntax.ParseExpression(rawBytes, "", hcl.Pos{})
if !diags.HasErrors() {
if lit, ok := parsed.(*hclsyntax.LiteralValueExpr); ok && lit.Val.RawEquals(value) {
return string(rawBytes)
}
}
}
switch value.Type() {
case cty.Bool:
if value.True() {
return "true"
}
return "false"
case cty.Number:
bf := value.AsBigFloat()
i, acc := bf.Int64()
if acc == big.Exact {
Enable perfsprint linter (#14813) <!--- Thanks so much for your contribution! If this is your first time contributing, please ensure that you have read the [CONTRIBUTING](https://github.com/pulumi/pulumi/blob/master/CONTRIBUTING.md) documentation. --> # Description <!--- Please include a summary of the change and which issue is fixed. Please also include relevant motivation and context. --> Prompted by a comment in another review: https://github.com/pulumi/pulumi/pull/14654#discussion_r1419995945 This lints that we don't use `fmt.Errorf` when `errors.New` will suffice, it also covers a load of other cases where `Sprintf` is sub-optimal. Most of these edits were made by running `perfsprint --fix`. ## Checklist - [x] I have run `make tidy` to update any new dependencies - [x] I have run `make lint` to verify my code passes the lint check - [x] I have formatted my code using `gofumpt` <!--- Please provide details if the checkbox below is to be left unchecked. --> - [ ] I have added tests that prove my fix is effective or that my feature works <!--- User-facing changes require a CHANGELOG entry. --> - [ ] I have run `make changelog` and committed the `changelog/pending/<file>` documenting my change <!-- If the change(s) in this PR is a modification of an existing call to the Pulumi Cloud, then the service should honor older versions of the CLI where this change would not exist. You must then bump the API version in /pkg/backend/httpstate/client/api.go, as well as add it to the service. --> - [ ] Yes, there are changes in this PR that warrants bumping the Pulumi Cloud API version <!-- @Pulumi employees: If yes, you must submit corresponding changes in the service repo. -->
2023-12-12 12:19:42 +00:00
return strconv.FormatInt(i, 10)
}
d, _ := bf.Float64()
return fmt.Sprintf("%g", d)
case cty.String:
if !escaped {
return value.AsString()
}
2022-12-02 14:42:20 +00:00
s := escapeString(value.AsString())
if quoted {
return fmt.Sprintf(`"%s"`, s)
}
return s
default:
panic(fmt.Errorf("unexpected literal type %v", value.Type().FriendlyName()))
}
}
2022-12-02 14:42:20 +00:00
func escapeString(s string) string {
// escape special characters
s = strconv.Quote(s)
s = s[1 : len(s)-1] // Remove surrounding double quote (`"`)
// Escape `${`
runes := []rune(s)
out := slice.Prealloc[rune](len(runes))
2022-12-02 14:42:20 +00:00
for i, r := range runes {
next := func() rune {
if i >= len(runes)-1 {
return 0
}
return runes[i+1]
}
if r == '$' && next() == '{' {
out = append(out, '$')
} else if r == '%' && next() == '{' {
out = append(out, '%')
}
out = append(out, r)
}
return string(out)
}
// LiteralValueExpression represents a semantically-analyzed literal value expression.
type LiteralValueExpression struct {
// The syntax node associated with the literal value expression.
Syntax *hclsyntax.LiteralValueExpr
// The tokens associated with the expression, if any.
Tokens *syntax.LiteralValueTokens
// The value of the expression.
Value cty.Value
exprType Type
}
// SyntaxNode returns the syntax node associated with the literal value expression.
func (x *LiteralValueExpression) SyntaxNode() hclsyntax.Node {
if x.Syntax == nil {
return syntax.None
}
return x.Syntax
}
// NodeTokens returns the tokens associated with the literal value expression.
func (x *LiteralValueExpression) NodeTokens() syntax.NodeTokens {
return x.Tokens
}
// Type returns the type of the literal value expression.
func (x *LiteralValueExpression) Type() Type {
if x.exprType == nil {
typ := ctyTypeToType(x.Value.Type(), false)
x.exprType = NewConstType(typ, x.Value)
}
return x.exprType
}
func (x *LiteralValueExpression) Typecheck(typecheckOperands bool) hcl.Diagnostics {
var diagnostics hcl.Diagnostics
typ := NoneType
if !x.Value.IsNull() {
typ = ctyTypeToType(x.Value.Type(), false)
}
switch {
case typ == NoneType || typ == StringType || typ == IntType || typ == NumberType || typ == BoolType:
// OK
typ = NewConstType(typ, x.Value)
default:
var rng hcl.Range
if x.Syntax != nil {
rng = x.Syntax.Range()
}
typ, diagnostics = DynamicType, hcl.Diagnostics{unsupportedLiteralValue(x.Value, rng)}
}
x.exprType = typ
return diagnostics
}
func (x *LiteralValueExpression) Evaluate(context *hcl.EvalContext) (cty.Value, hcl.Diagnostics) {
syntax := &hclsyntax.LiteralValueExpr{
Val: x.Value,
}
return syntax.Value(context)
}
func (x *LiteralValueExpression) HasLeadingTrivia() bool {
return exprHasLeadingTrivia(x.Tokens.GetParentheses(), x.Tokens != nil)
}
func (x *LiteralValueExpression) HasTrailingTrivia() bool {
return exprHasTrailingTrivia(x.Tokens.GetParentheses(), x.Tokens != nil)
}
func (x *LiteralValueExpression) GetLeadingTrivia() syntax.TriviaList {
if v := x.Tokens.GetValue(x.Value); len(v) > 0 {
return getExprLeadingTrivia(x.Tokens.GetParentheses(), v[0])
}
return getExprLeadingTrivia(x.Tokens.GetParentheses(), nil)
}
func (x *LiteralValueExpression) SetLeadingTrivia(t syntax.TriviaList) {
if x.Tokens == nil {
x.Tokens = syntax.NewLiteralValueTokens(x.Value)
}
setExprLeadingTrivia(x.Tokens.Parentheses, &x.Tokens.Value[0], t)
}
func (x *LiteralValueExpression) GetTrailingTrivia() syntax.TriviaList {
if v := x.Tokens.GetValue(x.Value); len(v) > 0 {
return getExprTrailingTrivia(x.Tokens.GetParentheses(), v[len(v)-1])
}
return getExprTrailingTrivia(x.Tokens.GetParentheses(), nil)
}
func (x *LiteralValueExpression) SetTrailingTrivia(t syntax.TriviaList) {
if x.Tokens == nil {
x.Tokens = syntax.NewLiteralValueTokens(x.Value)
}
setExprTrailingTrivia(x.Tokens.Parentheses, &x.Tokens.Value[len(x.Tokens.Value)-1], t)
}
func (x *LiteralValueExpression) Format(f fmt.State, c rune) {
x.print(f, &printer{})
}
func (x *LiteralValueExpression) printLit(w io.Writer, p *printer, escaped bool) {
// Literals are... odd. They may be composed of multiple tokens, but those tokens should never contain interior
// trivia.
var leading, trailing syntax.TriviaList
var rawBytes []byte
if toks := x.Tokens.GetValue(x.Value); len(toks) > 0 {
leading, trailing = toks[0].LeadingTrivia, toks[len(toks)-1].TrailingTrivia
for _, t := range toks {
rawBytes = append(rawBytes, t.Raw.Bytes...)
}
}
p.fprintf(w, "%(%v%v%v%)",
x.Tokens.GetParentheses(),
leading, literalText(x.Value, rawBytes, escaped, false), trailing,
x.Tokens.GetParentheses())
}
func (x *LiteralValueExpression) print(w io.Writer, p *printer) {
x.printLit(w, p, false)
}
func (*LiteralValueExpression) isExpression() {}
// ObjectConsItem records a key-value pair that is part of object construction expression.
type ObjectConsItem struct {
// The key.
Key Expression
// The value.
Value Expression
}
// ObjectConsExpression represents a semantically-analyzed object construction expression.
type ObjectConsExpression struct {
// The syntax node associated with the object construction expression.
Syntax *hclsyntax.ObjectConsExpr
// The tokens associated with the expression, if any.
Tokens *syntax.ObjectConsTokens
// The items that comprise the object construction expression.
Items []ObjectConsItem
exprType Type
}
// SyntaxNode returns the syntax node associated with the object construction expression.
func (x *ObjectConsExpression) SyntaxNode() hclsyntax.Node {
if x.Syntax == nil {
return syntax.None
}
return x.Syntax
}
// NodeTokens returns the tokens associated with the object construction expression.
func (x *ObjectConsExpression) NodeTokens() syntax.NodeTokens {
return x.Tokens
}
// Type returns the type of the object construction expression.
func (x *ObjectConsExpression) Type() Type {
return x.exprType
}
func (x *ObjectConsExpression) WithType(updateType func(Type) *ObjectConsExpression) *ObjectConsExpression {
return updateType(x.exprType)
}
func (x *ObjectConsExpression) Typecheck(typecheckOperands bool) hcl.Diagnostics {
var diagnostics hcl.Diagnostics
keys := slice.Prealloc[Expression](len(x.Items))
for _, item := range x.Items {
if typecheckOperands {
keyDiags := item.Key.Typecheck(true)
diagnostics = append(diagnostics, keyDiags...)
valDiags := item.Value.Typecheck(true)
diagnostics = append(diagnostics, valDiags...)
}
keys = append(keys, item.Key)
if !InputType(StringType).ConversionFrom(item.Key.Type()).Exists() {
diagnostics = append(diagnostics, objectKeysMustBeStrings(item.Key))
}
}
// Attempt to build an object type out of the result. If there are any attribute names that come from variables,
// type the result as map(unify(propertyTypes)).
properties, isMapType, types := map[string]Type{}, false, []Type{}
for _, item := range x.Items {
types = append(types, item.Value.Type())
key := item.Key
if template, ok := key.(*TemplateExpression); ok && len(template.Parts) == 1 {
key = template.Parts[0]
}
keyLit, ok := key.(*LiteralValueExpression)
if ok {
key, err := convert.Convert(keyLit.Value, cty.String)
if err == nil {
properties[key.AsString()] = item.Value.Type()
continue
}
}
isMapType = true
}
var typ Type
if isMapType {
elementType, _ := UnifyTypes(types...)
typ = NewMapType(elementType)
} else {
typ = NewObjectType(properties)
}
x.exprType = liftOperationType(typ, keys...)
return diagnostics
}
func (x *ObjectConsExpression) Evaluate(context *hcl.EvalContext) (cty.Value, hcl.Diagnostics) {
syntax := &hclsyntax.ObjectConsExpr{
Items: make([]hclsyntax.ObjectConsItem, len(x.Items)),
}
for i, item := range x.Items {
syntax.Items[i] = hclsyntax.ObjectConsItem{
KeyExpr: &syntaxExpr{expr: item.Key},
ValueExpr: &syntaxExpr{expr: item.Value},
}
}
return syntax.Value(context)
}
func (x *ObjectConsExpression) HasLeadingTrivia() bool {
return exprHasLeadingTrivia(x.Tokens.GetParentheses(), x.Tokens != nil)
}
func (x *ObjectConsExpression) HasTrailingTrivia() bool {
return exprHasTrailingTrivia(x.Tokens.GetParentheses(), x.Tokens != nil)
}
func (x *ObjectConsExpression) GetLeadingTrivia() syntax.TriviaList {
return getExprLeadingTrivia(x.Tokens.GetParentheses(), x.Tokens.GetOpenBrace(len(x.Items)))
}
func (x *ObjectConsExpression) SetLeadingTrivia(t syntax.TriviaList) {
if x.Tokens == nil {
x.Tokens = syntax.NewObjectConsTokens(len(x.Items))
}
setExprLeadingTrivia(x.Tokens.Parentheses, &x.Tokens.OpenBrace, t)
}
func (x *ObjectConsExpression) GetTrailingTrivia() syntax.TriviaList {
return getExprTrailingTrivia(x.Tokens.GetParentheses(), x.Tokens.GetCloseBrace())
}
func (x *ObjectConsExpression) SetTrailingTrivia(t syntax.TriviaList) {
if x.Tokens == nil {
x.Tokens = syntax.NewObjectConsTokens(len(x.Items))
}
setExprTrailingTrivia(x.Tokens.Parentheses, &x.Tokens.CloseBrace, t)
}
func (x *ObjectConsExpression) Format(f fmt.State, c rune) {
x.print(f, &printer{})
}
func (x *ObjectConsExpression) print(w io.Writer, p *printer) {
// Print the opening brace.
p.fprintf(w, "%(%v", x.Tokens.GetParentheses(), x.Tokens.GetOpenBrace(len(x.Items)))
// Print the items.
isMultiLine, trailingNewline := false, false
p.indented(func() {
items := x.Tokens.GetItems(len(x.Items))
for i, item := range x.Items {
tokens := syntax.NewObjectConsItemTokens(i == len(x.Items)-1)
if i < len(items) {
tokens = items[i]
}
if item.Key.HasLeadingTrivia() {
if _, i := item.Key.GetLeadingTrivia().Index("\n"); i != -1 {
isMultiLine = true
}
} else if len(items) > 1 {
isMultiLine = true
p.fprintf(w, "\n%s", p.indent)
}
p.fprintf(w, "%v% v% v", item.Key, tokens.Equals, item.Value)
if tokens.Comma != nil {
p.fprintf(w, "%v", tokens.Comma)
}
if isMultiLine && i == len(items)-1 {
trailingTrivia := item.Value.GetTrailingTrivia()
if tokens.Comma != nil {
trailingTrivia = tokens.Comma.TrailingTrivia
}
trailingNewline = trailingTrivia.EndsOnNewLine()
}
}
if len(x.Items) < len(items) {
for _, item := range items[len(x.Items):] {
p.fprintf(w, "%v", item.Equals.AllTrivia().CollapseWhitespace())
if item.Comma != nil {
p.fprintf(w, "%v", item.Comma.AllTrivia().CollapseWhitespace())
}
}
}
})
if x.Tokens != nil {
pre := ""
if isMultiLine && !trailingNewline {
pre = "\n" + p.indent
}
p.fprintf(w, "%s%v%)", pre, x.Tokens.CloseBrace, x.Tokens.Parentheses)
} else {
p.fprintf(w, "\n%s}", p.indent)
}
}
func (*ObjectConsExpression) isExpression() {}
func getTraverserTrivia(tokens syntax.TraverserTokens) (syntax.TriviaList, syntax.TriviaList) {
var leading, trailing syntax.TriviaList
switch tokens := tokens.(type) {
case *syntax.DotTraverserTokens:
leading = getExprLeadingTrivia(tokens.Parentheses, tokens.Dot)
trailing = getExprTrailingTrivia(tokens.Parentheses, tokens.Index)
case *syntax.BracketTraverserTokens:
leading = getExprLeadingTrivia(tokens.Parentheses, tokens.OpenBracket)
trailing = getExprTrailingTrivia(tokens.Parentheses, tokens.CloseBracket)
}
return leading, trailing
}
func setTraverserTrailingTrivia(tokens syntax.TraverserTokens, t syntax.TriviaList) {
switch tokens := tokens.(type) {
case *syntax.DotTraverserTokens:
setExprTrailingTrivia(tokens.Parentheses, &tokens.Index, t)
case *syntax.BracketTraverserTokens:
setExprTrailingTrivia(tokens.Parentheses, &tokens.CloseBracket, t)
default:
panic(fmt.Errorf("unexpected traverser of type %T", tokens))
}
}
func printTraverser(w io.Writer, p *printer, t hcl.Traverser, tokens syntax.TraverserTokens) {
var index string
switch t := t.(type) {
case hcl.TraverseAttr:
index = t.Name
case hcl.TraverseIndex:
index = literalText(t.Key, nil, true, true)
default:
panic(fmt.Errorf("unexpected traverser of type %T", t))
}
switch tokens := tokens.(type) {
case *syntax.DotTraverserTokens:
p.fprintf(w, "%(%v%v%)",
tokens.Parentheses,
tokens.Dot, identToken(tokens.Index, index),
tokens.Parentheses)
case *syntax.BracketTraverserTokens:
p.fprintf(w, "%(%v%v%v%)",
tokens.Parentheses,
tokens.OpenBracket, identToken(tokens.Index, index), tokens.CloseBracket,
tokens.Parentheses)
default:
panic(fmt.Errorf("unexpected traverser tokens of type %T", tokens))
}
}
func printRelativeTraversal(w io.Writer, p *printer, traversal hcl.Traversal, tokens []syntax.TraverserTokens) {
for i, traverser := range traversal {
// Fetch the traversal tokens.
var traverserTokens syntax.TraverserTokens
if i < len(tokens) {
traverserTokens = tokens[i]
}
printTraverser(w, p, traverser, traverserTokens)
}
// Print any remaining trivia.
if len(traversal) < len(tokens) {
for _, tokens := range tokens[len(traversal):] {
var trivia syntax.TriviaList
switch tokens := tokens.(type) {
case *syntax.DotTraverserTokens:
trivia = tokens.Dot.LeadingTrivia
trivia = append(trivia, tokens.Dot.TrailingTrivia...)
trivia = append(trivia, tokens.Index.LeadingTrivia...)
trivia = append(trivia, tokens.Index.TrailingTrivia...)
case *syntax.BracketTraverserTokens:
trivia = tokens.OpenBracket.LeadingTrivia
trivia = append(trivia, tokens.OpenBracket.TrailingTrivia...)
trivia = append(trivia, tokens.Index.LeadingTrivia...)
trivia = append(trivia, tokens.Index.TrailingTrivia...)
trivia = append(trivia, tokens.CloseBracket.LeadingTrivia...)
trivia = append(trivia, tokens.CloseBracket.TrailingTrivia...)
}
p.fprintf(w, "%v", trivia)
}
}
}
// RelativeTraversalExpression represents a semantically-analyzed relative traversal expression.
type RelativeTraversalExpression struct {
// The syntax node associated with the relative traversal expression.
Syntax *hclsyntax.RelativeTraversalExpr
// The tokens associated with the expression, if any.
Tokens *syntax.RelativeTraversalTokens
// The expression that computes the value being traversed.
Source Expression
// The traversal's parts.
Parts []Traversable
// The traversers.
Traversal hcl.Traversal
}
// SyntaxNode returns the syntax node associated with the relative traversal expression.
func (x *RelativeTraversalExpression) SyntaxNode() hclsyntax.Node {
if x.Syntax == nil {
return syntax.None
}
return x.Syntax
}
// NodeTokens returns the tokens associated with the relative traversal expression.
func (x *RelativeTraversalExpression) NodeTokens() syntax.NodeTokens {
return x.Tokens
}
// Type returns the type of the relative traversal expression.
func (x *RelativeTraversalExpression) Type() Type {
return GetTraversableType(x.Parts[len(x.Parts)-1])
}
func (x *RelativeTraversalExpression) typecheck(typecheckOperands, allowMissingVariables bool) hcl.Diagnostics {
var diagnostics hcl.Diagnostics
if typecheckOperands {
sourceDiags := x.Source.Typecheck(true)
diagnostics = append(diagnostics, sourceDiags...)
}
parts, partDiags := bindTraversalParts(x.Source.Type(), x.Traversal, allowMissingVariables)
diagnostics = append(diagnostics, partDiags...)
x.Parts = parts
return diagnostics
}
func (x *RelativeTraversalExpression) Typecheck(typecheckOperands bool) hcl.Diagnostics {
return x.typecheck(typecheckOperands, false)
}
func (x *RelativeTraversalExpression) Evaluate(context *hcl.EvalContext) (cty.Value, hcl.Diagnostics) {
syntax := &hclsyntax.RelativeTraversalExpr{
Source: &syntaxExpr{expr: x.Source},
Traversal: x.Traversal,
}
return syntax.Value(context)
}
func (x *RelativeTraversalExpression) HasLeadingTrivia() bool {
return exprHasLeadingTrivia(x.Tokens.GetParentheses(), x.Source)
}
func (x *RelativeTraversalExpression) HasTrailingTrivia() bool {
if parens := x.Tokens.GetParentheses(); parens.Any() {
return true
}
if x.Tokens != nil && len(x.Tokens.Traversal) > 0 {
return true
}
return x.Source.HasTrailingTrivia()
}
func (x *RelativeTraversalExpression) GetLeadingTrivia() syntax.TriviaList {
return getExprLeadingTrivia(x.Tokens.GetParentheses(), x.Source)
}
func (x *RelativeTraversalExpression) SetLeadingTrivia(t syntax.TriviaList) {
if x.Tokens == nil {
x.Tokens = syntax.NewRelativeTraversalTokens(x.Traversal)
}
setExprLeadingTrivia(x.Tokens.Parentheses, x.Source, t)
}
func (x *RelativeTraversalExpression) GetTrailingTrivia() syntax.TriviaList {
if parens := x.Tokens.GetParentheses(); parens.Any() {
return parens.GetTrailingTrivia()
}
if traversal := x.Tokens.GetTraversal(x.Traversal); len(traversal) > 0 {
_, trailingTrivia := getTraverserTrivia(traversal[len(traversal)-1])
return trailingTrivia
}
return x.Source.GetTrailingTrivia()
}
func (x *RelativeTraversalExpression) SetTrailingTrivia(t syntax.TriviaList) {
if x.Tokens == nil {
x.Tokens = syntax.NewRelativeTraversalTokens(x.Traversal)
}
if parens := x.Tokens.GetParentheses(); parens.Any() {
parens.SetTrailingTrivia(t)
return
}
if len(x.Tokens.Traversal) > 0 {
setTraverserTrailingTrivia(x.Tokens.Traversal[len(x.Tokens.Traversal)-1], t)
return
}
x.Source.SetTrailingTrivia(t)
}
func (x *RelativeTraversalExpression) Format(f fmt.State, c rune) {
x.print(f, &printer{})
}
func (x *RelativeTraversalExpression) print(w io.Writer, p *printer) {
// Print the source expression.
p.fprintf(w, "%(%v", x.Tokens.GetParentheses(), x.Source)
// Print the traversal.
printRelativeTraversal(w, p, x.Traversal, x.Tokens.GetTraversal(x.Traversal))
// Print the closing parentheses, if any.
p.fprintf(w, "%)", x.Tokens.GetParentheses())
}
func (*RelativeTraversalExpression) isExpression() {}
// ScopeTraversalExpression represents a semantically-analyzed scope traversal expression.
type ScopeTraversalExpression struct {
// The syntax node associated with the scope traversal expression.
Syntax *hclsyntax.ScopeTraversalExpr
// The tokens associated with the expression, if any.
Tokens *syntax.ScopeTraversalTokens
// The traversal's parts.
Parts []Traversable
// The root name.
RootName string
// The traversers.
Traversal hcl.Traversal
}
// SyntaxNode returns the syntax node associated with the scope traversal expression.
func (x *ScopeTraversalExpression) SyntaxNode() hclsyntax.Node {
if x.Syntax == nil {
return syntax.None
}
return x.Syntax
}
// NodeTokens returns the tokens associated with the scope traversal expression.
func (x *ScopeTraversalExpression) NodeTokens() syntax.NodeTokens {
return x.Tokens
}
// Type returns the type of the scope traversal expression.
func (x *ScopeTraversalExpression) Type() Type {
return GetTraversableType(x.Parts[len(x.Parts)-1])
}
func (x *ScopeTraversalExpression) typecheck(typecheckOperands, allowMissingVariables bool) hcl.Diagnostics {
parts, diagnostics := bindTraversalParts(x.Parts[0], x.Traversal.SimpleSplit().Rel, allowMissingVariables)
x.Parts = parts
return diagnostics
}
func (x *ScopeTraversalExpression) Typecheck(typecheckOperands bool) hcl.Diagnostics {
return x.typecheck(typecheckOperands, false)
}
func (x *ScopeTraversalExpression) Evaluate(context *hcl.EvalContext) (cty.Value, hcl.Diagnostics) {
var diagnostics hcl.Diagnostics
root, hasValue := x.Parts[0].(ValueTraversable)
if !hasValue {
return cty.UnknownVal(cty.DynamicPseudoType), nil
}
rootValue, diags := root.Value(context)
if diags.HasErrors() {
return cty.NilVal, diags
}
diagnostics = append(diagnostics, diags...)
if len(x.Traversal) == 1 {
return rootValue, diagnostics
}
return x.Traversal[1:].TraverseRel(rootValue)
}
func (x *ScopeTraversalExpression) HasLeadingTrivia() bool {
return exprHasLeadingTrivia(x.Tokens.GetParentheses(), x.Tokens != nil)
}
func (x *ScopeTraversalExpression) HasTrailingTrivia() bool {
if parens := x.Tokens.GetParentheses(); parens.Any() {
return true
}
if x.Tokens != nil && len(x.Tokens.Traversal) > 0 {
return true
}
return x.Tokens != nil
}
func (x *ScopeTraversalExpression) GetLeadingTrivia() syntax.TriviaList {
return getExprLeadingTrivia(x.Tokens.GetParentheses(), x.Tokens.GetRoot(x.Traversal))
}
func (x *ScopeTraversalExpression) SetLeadingTrivia(t syntax.TriviaList) {
if x.Tokens == nil {
x.Tokens = syntax.NewScopeTraversalTokens(x.Traversal)
}
x.Tokens.Root.LeadingTrivia = t
}
func (x *ScopeTraversalExpression) GetTrailingTrivia() syntax.TriviaList {
if parens := x.Tokens.GetParentheses(); parens.Any() {
return parens.GetTrailingTrivia()
}
if traversal := x.Tokens.GetTraversal(x.Traversal); len(traversal) > 0 {
_, trailingTrivia := getTraverserTrivia(traversal[len(traversal)-1])
return trailingTrivia
}
return x.Tokens.GetRoot(x.Traversal).TrailingTrivia
}
func (x *ScopeTraversalExpression) SetTrailingTrivia(t syntax.TriviaList) {
if x.Tokens == nil {
x.Tokens = syntax.NewScopeTraversalTokens(x.Traversal)
}
if parens := x.Tokens.GetParentheses(); parens.Any() {
parens.SetTrailingTrivia(t)
return
}
if len(x.Tokens.Traversal) > 0 {
setTraverserTrailingTrivia(x.Tokens.Traversal[len(x.Tokens.Traversal)-1], t)
return
}
x.Tokens.Root.TrailingTrivia = t
}
func (x *ScopeTraversalExpression) Format(f fmt.State, c rune) {
x.print(f, &printer{})
}
func (x *ScopeTraversalExpression) print(w io.Writer, p *printer) {
// Print the root name.
p.fprintf(w, "%(%v", x.Tokens.GetParentheses(), x.Tokens.GetRoot(x.Traversal))
// Print the traversal.
printRelativeTraversal(w, p, x.Traversal[1:], x.Tokens.GetTraversal(x.Traversal))
// Print the closing parentheses, if any.
p.fprintf(w, "%)", x.Tokens.GetParentheses())
}
func (*ScopeTraversalExpression) isExpression() {}
type SplatVariable struct {
Variable
symbol hclsyntax.AnonSymbolExpr
}
func (v *SplatVariable) Value(context *hcl.EvalContext) (cty.Value, hcl.Diagnostics) {
return (&v.symbol).Value(context)
}
// SplatExpression represents a semantically-analyzed splat expression.
type SplatExpression struct {
// The syntax node associated with the splat expression.
Syntax *hclsyntax.SplatExpr
// The tokens associated with the expression, if any.
Tokens *syntax.SplatTokens
// The expression being splatted.
Source Expression
// The expression applied to each element of the splat.
Each Expression
// The local variable definition associated with the current item being processed. This definition is not part of
// a scope, and can only be referenced by an AnonSymbolExpr.
Item *SplatVariable
exprType Type
}
// SyntaxNode returns the syntax node associated with the splat expression.
func (x *SplatExpression) SyntaxNode() hclsyntax.Node {
if x.Syntax == nil {
return syntax.None
}
return x.Syntax
}
// NodeTokens returns the tokens associated with the splat expression.
func (x *SplatExpression) NodeTokens() syntax.NodeTokens {
return x.Tokens
}
// Type returns the type of the splat expression.
func (x *SplatExpression) Type() Type {
return x.exprType
}
func splatItemType(source Expression, splatSyntax *hclsyntax.SplatExpr) (Expression, Type) {
sourceType := unwrapIterableSourceType(source.Type())
itemType := sourceType
switch sourceType := sourceType.(type) {
case *ListType:
itemType = sourceType.ElementType
case *SetType:
itemType = sourceType.ElementType
case *TupleType:
itemType, _ = UnifyTypes(sourceType.ElementTypes...)
default:
if sourceType != DynamicType {
var tupleSyntax *hclsyntax.TupleConsExpr
if splatSyntax != nil {
tupleSyntax = &hclsyntax.TupleConsExpr{
Exprs: []hclsyntax.Expression{splatSyntax.Source},
SrcRange: splatSyntax.Source.Range(),
OpenRange: splatSyntax.Source.StartRange(),
}
}
source = &TupleConsExpression{
Syntax: tupleSyntax,
Tokens: syntax.NewTupleConsTokens(1),
Expressions: []Expression{source},
exprType: NewListType(source.Type()),
}
}
}
return source, itemType
}
func (x *SplatExpression) typecheck(retypeItem, typecheckOperands bool) hcl.Diagnostics {
var diagnostics hcl.Diagnostics
if typecheckOperands {
sourceDiags := x.Source.Typecheck(true)
diagnostics = append(diagnostics, sourceDiags...)
}
if retypeItem {
x.Source, x.Item.VariableType = splatItemType(x.Source, x.Syntax)
}
if typecheckOperands {
eachDiags := x.Each.Typecheck(true)
diagnostics = append(diagnostics, eachDiags...)
}
x.exprType = wrapIterableResultType(x.Source.Type(), NewListType(x.Each.Type()))
return diagnostics
}
func (x *SplatExpression) Typecheck(typecheckOperands bool) hcl.Diagnostics {
return x.typecheck(true, typecheckOperands)
}
func (x *SplatExpression) Evaluate(context *hcl.EvalContext) (cty.Value, hcl.Diagnostics) {
syntax := &hclsyntax.SplatExpr{
Source: &syntaxExpr{expr: x.Source},
Each: &syntaxExpr{expr: x.Each},
Item: &x.Item.symbol,
}
return syntax.Value(context)
}
func (x *SplatExpression) HasLeadingTrivia() bool {
return exprHasLeadingTrivia(x.Tokens.GetParentheses(), x.Source)
}
func (x *SplatExpression) HasTrailingTrivia() bool {
return exprHasTrailingTrivia(x.Tokens.GetParentheses(), x.Each)
}
func (x *SplatExpression) GetLeadingTrivia() syntax.TriviaList {
return getExprLeadingTrivia(x.Tokens.GetParentheses(), x.Source)
}
func (x *SplatExpression) SetLeadingTrivia(t syntax.TriviaList) {
if x.Tokens == nil {
x.Tokens = syntax.NewSplatTokens(false)
}
setExprLeadingTrivia(x.Tokens.Parentheses, &x.Tokens.Open, t)
}
func (x *SplatExpression) GetTrailingTrivia() syntax.TriviaList {
if parens := x.Tokens.GetParentheses(); parens.Any() {
return parens.GetTrailingTrivia()
}
all: Fix revive issues Fixes the following issues found by revive included in the latest release of golangci-lint. Full list of issues: **pkg** ``` backend/display/object_diff.go:47:10: superfluous-else: if block ends with a break statement, so drop this else and outdent its block (move short variable declaration to its own line if necessary) (revive) backend/display/object_diff.go:716:12: redefines-builtin-id: redefinition of the built-in function delete (revive) backend/display/object_diff.go:742:14: redefines-builtin-id: redefinition of the built-in function delete (revive) backend/display/object_diff.go:983:10: superfluous-else: if block ends with a continue statement, so drop this else and outdent its block (revive) backend/httpstate/backend.go:1814:4: redefines-builtin-id: redefinition of the built-in function cap (revive) backend/httpstate/backend.go:1824:5: redefines-builtin-id: redefinition of the built-in function cap (revive) backend/httpstate/client/client.go:444:2: if-return: redundant if ...; err != nil check, just return error instead. (revive) backend/httpstate/client/client.go:455:2: if-return: redundant if ...; err != nil check, just return error instead. (revive) cmd/pulumi/org.go:113:4: if-return: redundant if ...; err != nil check, just return error instead. (revive) cmd/pulumi/util.go:216:2: if-return: redundant if ...; err != nil check, just return error instead. (revive) codegen/docs/gen.go:428:2: redefines-builtin-id: redefinition of the built-in function copy (revive) codegen/hcl2/model/expression.go:2151:5: redefines-builtin-id: redefinition of the built-in function close (revive) codegen/hcl2/syntax/comments.go:151:2: redefines-builtin-id: redefinition of the built-in function close (revive) codegen/hcl2/syntax/comments.go:329:3: redefines-builtin-id: redefinition of the built-in function close (revive) codegen/hcl2/syntax/comments.go:381:5: redefines-builtin-id: redefinition of the built-in function close (revive) codegen/nodejs/gen.go:1367:5: redefines-builtin-id: redefinition of the built-in function copy (revive) codegen/python/gen_program_expressions.go:136:2: redefines-builtin-id: redefinition of the built-in function close (revive) codegen/python/gen_program_expressions.go:142:3: redefines-builtin-id: redefinition of the built-in function close (revive) codegen/report/report.go:126:6: redefines-builtin-id: redefinition of the built-in function panic (revive) codegen/schema/docs_test.go:210:10: superfluous-else: if block ends with a continue statement, so drop this else and outdent its block (move short variable declaration to its own line if necessary) (revive) codegen/schema/schema.go:790:2: redefines-builtin-id: redefinition of the built-in type any (revive) codegen/schema/schema.go:793:4: redefines-builtin-id: redefinition of the built-in type any (revive) resource/deploy/plan.go:506:2: if-return: redundant if ...; err != nil check, just return error instead. (revive) resource/deploy/snapshot_test.go:59:3: redefines-builtin-id: redefinition of the built-in function copy (revive) resource/deploy/state_builder.go:108:2: redefines-builtin-id: redefinition of the built-in function copy (revive) ``` **sdk** ``` go/common/resource/plugin/context.go:142:2: redefines-builtin-id: redefinition of the built-in function copy (revive) go/common/resource/plugin/plugin.go:142:12: superfluous-else: if block ends with a break statement, so drop this else and outdent its block (revive) go/common/resource/properties_diff.go:114:2: redefines-builtin-id: redefinition of the built-in function len (revive) go/common/resource/properties_diff.go:117:4: redefines-builtin-id: redefinition of the built-in function len (revive) go/common/resource/properties_diff.go:122:4: redefines-builtin-id: redefinition of the built-in function len (revive) go/common/resource/properties_diff.go:127:4: redefines-builtin-id: redefinition of the built-in function len (revive) go/common/resource/properties_diff.go:132:4: redefines-builtin-id: redefinition of the built-in function len (revive) go/common/util/deepcopy/copy.go:30:1: redefines-builtin-id: redefinition of the built-in function copy (revive) go/common/workspace/creds.go:242:2: if-return: redundant if ...; err != nil check, just return error instead. (revive) go/pulumi-language-go/main.go:569:2: if-return: redundant if ...; err != nil check, just return error instead. (revive) go/pulumi-language-go/main.go:706:2: if-return: redundant if ...; err != nil check, just return error instead. (revive) go/pulumi/run_test.go:925:2: redefines-builtin-id: redefinition of the built-in type any (revive) go/pulumi/run_test.go:933:3: redefines-builtin-id: redefinition of the built-in type any (revive) nodejs/cmd/pulumi-language-nodejs/main.go:778:2: if-return: redundant if ...; err != nil check, just return error instead. (revive) python/cmd/pulumi-language-python/main.go:1011:2: if-return: redundant if ...; err != nil check, just return error instead. (revive) python/cmd/pulumi-language-python/main.go:863:2: if-return: redundant if ...; err != nil check, just return error instead. (revive) python/python.go:230:2: redefines-builtin-id: redefinition of the built-in function print (revive) ``` **tests** ``` integration/integration_util_test.go:282:11: superfluous-else: if block ends with a continue statement, so drop this else and outdent its block (move short variable declaration to its own line if necessary) (revive) ```
2023-03-20 23:48:02 +00:00
if closeTok := x.Tokens.GetClose(); closeTok != nil {
return closeTok.TrailingTrivia
}
return x.Tokens.GetStar().TrailingTrivia
}
func (x *SplatExpression) SetTrailingTrivia(t syntax.TriviaList) {
if x.Tokens == nil {
x.Tokens = syntax.NewSplatTokens(false)
}
if x.Tokens.Parentheses.Any() {
x.Tokens.Parentheses.SetTrailingTrivia(t)
return
}
if x.Tokens.Close == nil {
x.Tokens.Star.TrailingTrivia = t
return
}
x.Tokens.Close.TrailingTrivia = t
}
func (x *SplatExpression) Format(f fmt.State, c rune) {
x.print(f, &printer{})
}
func (x *SplatExpression) print(w io.Writer, p *printer) {
isDot := x.Tokens.GetClose() == nil
p.fprintf(w, "%(%v%v%v", x.Tokens.GetParentheses(), x.Source, x.Tokens.GetOpen(), x.Tokens.GetStar())
if !isDot {
p.fprintf(w, "%v", x.Tokens.GetClose())
}
p.fprintf(w, "%v%)", x.Each, x.Tokens.GetParentheses())
}
func (*SplatExpression) isExpression() {}
// TemplateExpression represents a semantically-analyzed template expression.
type TemplateExpression struct {
// The syntax node associated with the template expression.
Syntax *hclsyntax.TemplateExpr
// The tokens associated with the expression, if any.
Tokens *syntax.TemplateTokens
// The parts of the template expression.
Parts []Expression
exprType Type
}
// SyntaxNode returns the syntax node associated with the template expression.
func (x *TemplateExpression) SyntaxNode() hclsyntax.Node {
if x.Syntax == nil {
return syntax.None
}
return x.Syntax
}
// NodeTokens returns the tokens associated with the template expression.
func (x *TemplateExpression) NodeTokens() syntax.NodeTokens {
return x.Tokens
}
// Type returns the type of the template expression.
func (x *TemplateExpression) Type() Type {
return x.exprType
}
func (x *TemplateExpression) Typecheck(typecheckOperands bool) hcl.Diagnostics {
var diagnostics hcl.Diagnostics
if typecheckOperands {
for _, part := range x.Parts {
partDiags := part.Typecheck(true)
diagnostics = append(diagnostics, partDiags...)
}
}
x.exprType = liftOperationType(StringType, x.Parts...)
return diagnostics
}
func (x *TemplateExpression) Evaluate(context *hcl.EvalContext) (cty.Value, hcl.Diagnostics) {
syntax := &hclsyntax.TemplateExpr{
Parts: make([]hclsyntax.Expression, len(x.Parts)),
}
for i, p := range x.Parts {
syntax.Parts[i] = &syntaxExpr{expr: p}
}
return syntax.Value(context)
}
func (x *TemplateExpression) HasLeadingTrivia() bool {
return exprHasLeadingTrivia(x.Tokens.GetParentheses(), x.Tokens != nil)
}
func (x *TemplateExpression) HasTrailingTrivia() bool {
return exprHasTrailingTrivia(x.Tokens.GetParentheses(), x.Tokens != nil)
}
func (x *TemplateExpression) GetLeadingTrivia() syntax.TriviaList {
return getExprLeadingTrivia(x.Tokens.GetParentheses(), x.Tokens.GetOpen())
}
func (x *TemplateExpression) SetLeadingTrivia(t syntax.TriviaList) {
if x.Tokens == nil {
x.Tokens = syntax.NewTemplateTokens()
}
setExprLeadingTrivia(x.Tokens.Parentheses, &x.Tokens.Open, t)
}
func (x *TemplateExpression) GetTrailingTrivia() syntax.TriviaList {
return getExprTrailingTrivia(x.Tokens.GetParentheses(), x.Tokens.GetClose())
}
func (x *TemplateExpression) SetTrailingTrivia(t syntax.TriviaList) {
if x.Tokens == nil {
x.Tokens = syntax.NewTemplateTokens()
}
setExprTrailingTrivia(x.Tokens.Parentheses, &x.Tokens.Close, t)
}
func (x *TemplateExpression) Format(f fmt.State, c rune) {
x.print(f, &printer{})
}
func (x *TemplateExpression) print(w io.Writer, p *printer) {
// Print the opening quote.
p.fprintf(w, "%(%v", x.Tokens.GetParentheses(), x.Tokens.GetOpen())
isHeredoc := x.Tokens.GetOpen().Raw.Type == hclsyntax.TokenOHeredoc
// Print the expressions.
for _, part := range x.Parts {
if lit, ok := part.(*LiteralValueExpression); ok && StringType.AssignableFrom(lit.Type()) {
lit.printLit(w, p, !isHeredoc)
} else {
p.fprintf(w, "%v", part)
}
}
// Print the closing quote
p.fprintf(w, "%v%)", x.Tokens.GetClose(), x.Tokens.GetParentheses())
}
func (*TemplateExpression) isExpression() {}
// TemplateJoinExpression represents a semantically-analyzed template join expression.
type TemplateJoinExpression struct {
// The syntax node associated with the template join expression.
Syntax *hclsyntax.TemplateJoinExpr
// The tuple being joined.
Tuple Expression
exprType Type
}
// SyntaxNode returns the syntax node associated with the template join expression.
func (x *TemplateJoinExpression) SyntaxNode() hclsyntax.Node {
if x.Syntax == nil {
return syntax.None
}
return x.Syntax
}
// NodeTokens returns the tokens associated with the template join expression.
func (x *TemplateJoinExpression) NodeTokens() syntax.NodeTokens {
return nil
}
// Type returns the type of the template join expression.
func (x *TemplateJoinExpression) Type() Type {
return x.exprType
}
func (x *TemplateJoinExpression) Typecheck(typecheckOperands bool) hcl.Diagnostics {
var diagnostics hcl.Diagnostics
if typecheckOperands {
tupleDiags := x.Tuple.Typecheck(true)
diagnostics = append(diagnostics, tupleDiags...)
}
x.exprType = liftOperationType(StringType, x.Tuple)
return diagnostics
}
func (x *TemplateJoinExpression) Evaluate(context *hcl.EvalContext) (cty.Value, hcl.Diagnostics) {
syntax := &hclsyntax.TemplateJoinExpr{
Tuple: &syntaxExpr{expr: x.Tuple},
}
return syntax.Value(context)
}
func (x *TemplateJoinExpression) HasLeadingTrivia() bool {
return x.Tuple.HasLeadingTrivia()
}
func (x *TemplateJoinExpression) HasTrailingTrivia() bool {
return x.Tuple.HasTrailingTrivia()
}
func (x *TemplateJoinExpression) GetLeadingTrivia() syntax.TriviaList {
return x.Tuple.GetLeadingTrivia()
}
func (x *TemplateJoinExpression) SetLeadingTrivia(t syntax.TriviaList) {
x.Tuple.SetLeadingTrivia(t)
}
func (x *TemplateJoinExpression) GetTrailingTrivia() syntax.TriviaList {
return x.Tuple.GetTrailingTrivia()
}
func (x *TemplateJoinExpression) SetTrailingTrivia(t syntax.TriviaList) {
x.Tuple.SetTrailingTrivia(t)
}
func (x *TemplateJoinExpression) Format(f fmt.State, c rune) {
x.print(f, &printer{})
}
func (x *TemplateJoinExpression) print(w io.Writer, p *printer) {
p.fprintf(w, "%v", x.Tuple)
}
func (*TemplateJoinExpression) isExpression() {}
// TupleConsExpression represents a semantically-analyzed tuple construction expression.
type TupleConsExpression struct {
// The syntax node associated with the tuple construction expression.
Syntax *hclsyntax.TupleConsExpr
// The tokens associated with the expression, if any.
Tokens *syntax.TupleConsTokens
// The elements of the tuple.
Expressions []Expression
exprType Type
}
// SyntaxNode returns the syntax node associated with the tuple construction expression.
func (x *TupleConsExpression) SyntaxNode() hclsyntax.Node {
if x.Syntax == nil {
return syntax.None
}
return x.Syntax
}
// NodeTokens returns the tokens associated with the tuple construction expression.
func (x *TupleConsExpression) NodeTokens() syntax.NodeTokens {
return x.Tokens
}
// Type returns the type of the tuple construction expression.
func (x *TupleConsExpression) Type() Type {
return x.exprType
}
func (x *TupleConsExpression) Typecheck(typecheckOperands bool) hcl.Diagnostics {
var diagnostics hcl.Diagnostics
elementTypes := make([]Type, len(x.Expressions))
for i, expr := range x.Expressions {
if typecheckOperands {
exprDiags := expr.Typecheck(true)
diagnostics = append(diagnostics, exprDiags...)
}
elementTypes[i] = expr.Type()
}
x.exprType = NewTupleType(elementTypes...)
return diagnostics
}
func (x *TupleConsExpression) Evaluate(context *hcl.EvalContext) (cty.Value, hcl.Diagnostics) {
syntax := &hclsyntax.TupleConsExpr{
Exprs: make([]hclsyntax.Expression, len(x.Expressions)),
}
for i, x := range x.Expressions {
syntax.Exprs[i] = &syntaxExpr{expr: x}
}
return syntax.Value(context)
}
func (x *TupleConsExpression) HasLeadingTrivia() bool {
return exprHasLeadingTrivia(x.Tokens.GetParentheses(), x.Tokens != nil)
}
func (x *TupleConsExpression) HasTrailingTrivia() bool {
return exprHasTrailingTrivia(x.Tokens.GetParentheses(), x.Tokens != nil)
}
func (x *TupleConsExpression) GetLeadingTrivia() syntax.TriviaList {
return getExprLeadingTrivia(x.Tokens.GetParentheses(), x.Tokens.GetOpenBracket())
}
func (x *TupleConsExpression) SetLeadingTrivia(t syntax.TriviaList) {
if x.Tokens == nil {
x.Tokens = syntax.NewTupleConsTokens(len(x.Expressions))
}
setExprLeadingTrivia(x.Tokens.Parentheses, &x.Tokens.OpenBracket, t)
}
func (x *TupleConsExpression) GetTrailingTrivia() syntax.TriviaList {
return getExprTrailingTrivia(x.Tokens.GetParentheses(), x.Tokens.GetCloseBracket())
}
func (x *TupleConsExpression) SetTrailingTrivia(t syntax.TriviaList) {
if x.Tokens == nil {
x.Tokens = syntax.NewTupleConsTokens(len(x.Expressions))
}
setExprTrailingTrivia(x.Tokens.Parentheses, &x.Tokens.CloseBracket, t)
}
func (x *TupleConsExpression) Format(f fmt.State, c rune) {
x.print(f, &printer{})
}
func (x *TupleConsExpression) print(w io.Writer, p *printer) {
// Print the opening bracket.
p.fprintf(w, "%(%v", x.Tokens.GetParentheses(), x.Tokens.GetOpenBracket())
// Print each element and its comma.
commas := x.Tokens.GetCommas(len(x.Expressions))
p.indented(func() {
for i, expr := range x.Expressions {
if !expr.HasLeadingTrivia() {
p.fprintf(w, "\n%s", p.indent)
}
p.fprintf(w, "%v", expr)
if i != len(x.Expressions)-1 {
var comma syntax.Token
if i < len(commas) {
comma = commas[i]
}
p.fprintf(w, "%v", comma)
}
}
// If there were commas left over, print the trivia for each.
//
// TODO(pdg): filter to only comments?
if len(x.Expressions) > 0 && len(x.Expressions)-1 <= len(commas) {
for _, comma := range commas[len(x.Expressions)-1:] {
p.fprintf(w, "%v", comma.AllTrivia().CollapseWhitespace())
}
}
})
// Print the closing bracket.
if x.Tokens != nil {
p.fprintf(w, "%v%)", x.Tokens.CloseBracket, x.Tokens.GetParentheses())
} else {
p.fprintf(w, "\n%s]", p.indent)
}
}
func (*TupleConsExpression) isExpression() {}
// UnaryOpExpression represents a semantically-analyzed unary operation.
type UnaryOpExpression struct {
// The syntax node associated with the unary operation.
Syntax *hclsyntax.UnaryOpExpr
// The tokens associated with the expression, if any.
Tokens *syntax.UnaryOpTokens
// The operation.
Operation *hclsyntax.Operation
// The operand of the operation.
Operand Expression
operandType Type
exprType Type
}
// SyntaxNode returns the syntax node associated with the unary operation.
func (x *UnaryOpExpression) SyntaxNode() hclsyntax.Node {
if x.Syntax == nil {
return syntax.None
}
return x.Syntax
}
// NodeTokens returns the tokens associated with the unary operation.
func (x *UnaryOpExpression) NodeTokens() syntax.NodeTokens {
return x.Tokens
}
// OperandType returns the operand type of the unary operation.
func (x *UnaryOpExpression) OperandType() Type {
return x.operandType
}
// Type returns the type of the unary operation.
func (x *UnaryOpExpression) Type() Type {
return x.exprType
}
func (x *UnaryOpExpression) Typecheck(typecheckOperands bool) hcl.Diagnostics {
var diagnostics hcl.Diagnostics
if typecheckOperands {
operandDiags := x.Operand.Typecheck(true)
diagnostics = append(diagnostics, operandDiags...)
}
// Compute the signature for the operator and typecheck the arguments.
signature := getOperationSignature(x.Operation)
contract.Assertf(len(signature.Parameters) == 1,
"expected unary operator signature to have 1 parameter, got %d", len(signature.Parameters))
x.operandType = signature.Parameters[0].Type
var rng hcl.Range
if x.Syntax != nil {
rng = x.Syntax.Range()
}
typecheckDiags := typecheckArgs(rng, signature, x.Operand)
diagnostics = append(diagnostics, typecheckDiags...)
x.exprType = liftOperationType(signature.ReturnType, x.Operand)
return diagnostics
}
func (x *UnaryOpExpression) Evaluate(context *hcl.EvalContext) (cty.Value, hcl.Diagnostics) {
syntax := &hclsyntax.UnaryOpExpr{
Op: x.Operation,
Val: &syntaxExpr{expr: x.Operand},
}
return syntax.Value(context)
}
func (x *UnaryOpExpression) HasLeadingTrivia() bool {
return exprHasLeadingTrivia(x.Tokens.GetParentheses(), x.Tokens != nil)
}
func (x *UnaryOpExpression) HasTrailingTrivia() bool {
return exprHasTrailingTrivia(x.Tokens.GetParentheses(), x.Operand)
}
func (x *UnaryOpExpression) GetLeadingTrivia() syntax.TriviaList {
return getExprLeadingTrivia(x.Tokens.GetParentheses(), x.Tokens.GetOperator(x.Operation))
}
func (x *UnaryOpExpression) SetLeadingTrivia(t syntax.TriviaList) {
if x.Tokens == nil {
x.Tokens = syntax.NewUnaryOpTokens(x.Operation)
}
setExprLeadingTrivia(x.Tokens.Parentheses, &x.Tokens.Operator, t)
}
func (x *UnaryOpExpression) GetTrailingTrivia() syntax.TriviaList {
return getExprTrailingTrivia(x.Tokens.GetParentheses(), x.Operand)
}
func (x *UnaryOpExpression) SetTrailingTrivia(t syntax.TriviaList) {
if x.Tokens == nil {
x.Tokens = syntax.NewUnaryOpTokens(x.Operation)
}
setExprTrailingTrivia(x.Tokens.Parentheses, x.Operand, t)
}
func (x *UnaryOpExpression) Format(f fmt.State, c rune) {
x.print(f, &printer{})
}
func (x *UnaryOpExpression) print(w io.Writer, p *printer) {
precedence := operatorPrecedence(x.Operation)
p.fprintf(w, "%[2](%[3]v%.[1]*[4]v%[5])",
precedence,
x.Tokens.GetParentheses(),
x.Tokens.GetOperator(x.Operation), x.Operand,
x.Tokens.GetParentheses())
}
func (*UnaryOpExpression) isExpression() {}