2024-09-09 12:05:45 +00:00
|
|
|
// Copyright 2020-2024, 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.
|
|
|
|
|
2020-05-21 17:23:33 +00:00
|
|
|
package gen
|
|
|
|
|
|
|
|
import (
|
2020-06-06 01:52:00 +00:00
|
|
|
"bytes"
|
2023-12-12 12:19:42 +00:00
|
|
|
"errors"
|
2020-05-21 17:23:33 +00:00
|
|
|
"fmt"
|
|
|
|
"io"
|
|
|
|
"math/big"
|
|
|
|
"strings"
|
|
|
|
|
|
|
|
"github.com/hashicorp/hcl/v2"
|
|
|
|
"github.com/hashicorp/hcl/v2/hclsyntax"
|
2021-06-24 16:17:55 +00:00
|
|
|
"github.com/pulumi/pulumi/pkg/v3/codegen"
|
2021-03-17 13:20:05 +00:00
|
|
|
"github.com/pulumi/pulumi/pkg/v3/codegen/hcl2/model"
|
2021-09-30 03:11:56 +00:00
|
|
|
"github.com/pulumi/pulumi/pkg/v3/codegen/pcl"
|
2021-03-17 13:20:05 +00:00
|
|
|
"github.com/pulumi/pulumi/pkg/v3/codegen/schema"
|
2023-06-28 16:02:04 +00:00
|
|
|
"github.com/pulumi/pulumi/sdk/v3/go/common/slice"
|
2021-03-17 13:20:05 +00:00
|
|
|
"github.com/pulumi/pulumi/sdk/v3/go/common/util/contract"
|
2020-05-21 17:23:33 +00:00
|
|
|
"github.com/zclconf/go-cty/cty"
|
|
|
|
)
|
|
|
|
|
2020-06-30 01:10:34 +00:00
|
|
|
const keywordRange = "range"
|
|
|
|
|
2020-05-21 17:23:33 +00:00
|
|
|
func (g *generator) GetPrecedence(expr model.Expression) int {
|
|
|
|
// TODO: Current values copied from Node, update based on
|
|
|
|
// https://golang.org/ref/spec
|
|
|
|
switch expr := expr.(type) {
|
|
|
|
case *model.ConditionalExpression:
|
|
|
|
return 4
|
|
|
|
case *model.BinaryOpExpression:
|
|
|
|
switch expr.Operation {
|
|
|
|
case hclsyntax.OpLogicalOr:
|
|
|
|
return 5
|
|
|
|
case hclsyntax.OpLogicalAnd:
|
|
|
|
return 6
|
|
|
|
case hclsyntax.OpEqual, hclsyntax.OpNotEqual:
|
|
|
|
return 11
|
|
|
|
case hclsyntax.OpGreaterThan, hclsyntax.OpGreaterThanOrEqual, hclsyntax.OpLessThan,
|
|
|
|
hclsyntax.OpLessThanOrEqual:
|
|
|
|
return 12
|
|
|
|
case hclsyntax.OpAdd, hclsyntax.OpSubtract:
|
|
|
|
return 14
|
|
|
|
case hclsyntax.OpMultiply, hclsyntax.OpDivide, hclsyntax.OpModulo:
|
|
|
|
return 15
|
|
|
|
default:
|
|
|
|
contract.Failf("unexpected binary expression %v", expr)
|
|
|
|
}
|
|
|
|
case *model.UnaryOpExpression:
|
|
|
|
return 17
|
|
|
|
case *model.FunctionCallExpression:
|
|
|
|
switch expr.Name {
|
|
|
|
default:
|
|
|
|
return 20
|
|
|
|
}
|
|
|
|
case *model.ForExpression, *model.IndexExpression, *model.RelativeTraversalExpression, *model.SplatExpression,
|
|
|
|
*model.TemplateJoinExpression:
|
|
|
|
return 20
|
|
|
|
case *model.AnonymousFunctionExpression, *model.LiteralValueExpression, *model.ObjectConsExpression,
|
|
|
|
*model.ScopeTraversalExpression, *model.TemplateExpression, *model.TupleConsExpression:
|
|
|
|
return 22
|
|
|
|
default:
|
|
|
|
contract.Failf("unexpected expression %v of type %T", expr, expr)
|
|
|
|
}
|
|
|
|
return 0
|
|
|
|
}
|
|
|
|
|
|
|
|
// GenAnonymousFunctionExpression generates code for an AnonymousFunctionExpression.
|
2020-05-26 14:07:54 +00:00
|
|
|
func (g *generator) GenAnonymousFunctionExpression(w io.Writer, expr *model.AnonymousFunctionExpression) {
|
2021-06-24 16:17:55 +00:00
|
|
|
g.genAnonymousFunctionExpression(w, expr, nil, false)
|
2020-06-19 00:17:51 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (g *generator) genAnonymousFunctionExpression(
|
|
|
|
w io.Writer,
|
|
|
|
expr *model.AnonymousFunctionExpression,
|
|
|
|
bodyPreamble []string,
|
2021-06-24 16:17:55 +00:00
|
|
|
inApply bool,
|
2020-06-19 00:17:51 +00:00
|
|
|
) {
|
2020-05-26 14:07:54 +00:00
|
|
|
g.Fgenf(w, "func(")
|
|
|
|
leadingSep := ""
|
|
|
|
for _, param := range expr.Signature.Parameters {
|
2020-06-06 01:52:00 +00:00
|
|
|
isInput := isInputty(param.Type)
|
2024-07-23 07:03:26 +00:00
|
|
|
g.Fgenf(w, "%s%s %s", leadingSep, makeValidIdentifier(param.Name), g.argumentTypeName(param.Type, isInput))
|
2020-05-26 14:07:54 +00:00
|
|
|
leadingSep = ", "
|
|
|
|
}
|
|
|
|
|
2021-06-24 16:17:55 +00:00
|
|
|
retType := expr.Signature.ReturnType
|
|
|
|
if inApply {
|
|
|
|
retType = model.ResolveOutputs(retType)
|
|
|
|
}
|
|
|
|
|
2024-07-23 07:03:26 +00:00
|
|
|
retTypeName := g.argumentTypeName(retType, false)
|
2021-06-24 16:17:55 +00:00
|
|
|
g.Fgenf(w, ") (%s, error) {\n", retTypeName)
|
2020-06-16 06:00:02 +00:00
|
|
|
|
2020-06-19 00:17:51 +00:00
|
|
|
for _, decl := range bodyPreamble {
|
|
|
|
g.Fgenf(w, "%s\n", decl)
|
|
|
|
}
|
|
|
|
|
2021-06-24 16:17:55 +00:00
|
|
|
body, temps := g.lowerExpression(expr.Body, retType)
|
|
|
|
g.genTempsMultiReturn(w, temps, retTypeName)
|
2020-06-16 06:00:02 +00:00
|
|
|
|
2022-09-16 23:12:29 +00:00
|
|
|
// g.Fgenf(w, "return %v, nil", body)
|
|
|
|
|
|
|
|
// fromBase64 special case
|
|
|
|
if b, ok := body.(*model.FunctionCallExpression); ok && b.Name == fromBase64Fn {
|
|
|
|
g.Fgenf(w, "value, _ := %v\n", b)
|
|
|
|
g.Fgenf(w, "return pulumi.String(value), nil")
|
|
|
|
} else if strings.HasPrefix(retTypeName, "pulumi") {
|
|
|
|
g.Fgenf(w, "return %s(%v), nil", retTypeName, body)
|
|
|
|
} else {
|
|
|
|
g.Fgenf(w, "return %v, nil", body)
|
|
|
|
}
|
2020-05-26 14:07:54 +00:00
|
|
|
g.Fgenf(w, "\n}")
|
2020-05-21 17:23:33 +00:00
|
|
|
}
|
|
|
|
|
2020-05-28 20:47:49 +00:00
|
|
|
func (g *generator) GenBinaryOpExpression(w io.Writer, expr *model.BinaryOpExpression) {
|
Enable some more linting rules (#17456)
Issue #10659 lists a number of extra linting checks that we could enable
in order to make our Go code more robust. This commit implements as many
as seem sensible:
* `durationcheck`, which checks for multiplication of `time.Duration`s,
which can lead to unexpected behaviour (e.g. `time.Second * time.Second`
is *not* one second)
* `goprintffuncname`, which checks that `Printf`-like functions are
appropriately suffixed with `f` to indicate as such
* `tenv`, which checks for `os.Setenv` in tests where `t.Setenv` is
generally a better solution
* `wastedassign`, which checks for assignments whose values are never
used (such as initial values before an `if` where both branches then
overwrite the value)
* `whitespace`, which checks for blank lines at the beginning and end of
blocks such as functions, `if`s, `for`s and so on.
This commit does *not* enable the following checks listed in #10659:
* `wrapcheck`, which insists that third-party library errors are always
`%w`rapped -- we have a lot of cases where we don't do this and it's
probably a bit more involved than "just wrap them" in terms of making
sure we don't break anything (maybe)
* `predeclared`, which checks for shadowing of existing Go identifiers
-- we use `old` and `new` a lot, especially in step generation, so this
is probably a slightly bigger clean-up/one we might want to opt out of
* `mnd` (magic number detection) -- we have a lot of failures on this
* `nilnil` -- we only have a couple of failures on this; these could
probably be handled with `//nolint` but for now I've opted not to take
this route.
2024-10-03 17:37:13 +00:00
|
|
|
var opstr string
|
|
|
|
precedence := g.GetPrecedence(expr)
|
2020-05-28 20:47:49 +00:00
|
|
|
switch expr.Operation {
|
|
|
|
case hclsyntax.OpAdd:
|
|
|
|
opstr = "+"
|
|
|
|
case hclsyntax.OpDivide:
|
|
|
|
opstr = "/"
|
|
|
|
case hclsyntax.OpEqual:
|
|
|
|
opstr = "=="
|
|
|
|
case hclsyntax.OpGreaterThan:
|
|
|
|
opstr = ">"
|
|
|
|
case hclsyntax.OpGreaterThanOrEqual:
|
|
|
|
opstr = ">="
|
|
|
|
case hclsyntax.OpLessThan:
|
|
|
|
opstr = "<"
|
|
|
|
case hclsyntax.OpLessThanOrEqual:
|
|
|
|
opstr = "<="
|
|
|
|
case hclsyntax.OpLogicalAnd:
|
|
|
|
opstr = "&&"
|
|
|
|
case hclsyntax.OpLogicalOr:
|
|
|
|
opstr = "||"
|
|
|
|
case hclsyntax.OpModulo:
|
|
|
|
opstr = "%"
|
|
|
|
case hclsyntax.OpMultiply:
|
|
|
|
opstr = "*"
|
|
|
|
case hclsyntax.OpNotEqual:
|
|
|
|
opstr = "!="
|
|
|
|
case hclsyntax.OpSubtract:
|
|
|
|
opstr = "-"
|
|
|
|
default:
|
|
|
|
opstr, precedence = ",", 1
|
|
|
|
}
|
2020-05-21 17:23:33 +00:00
|
|
|
|
2020-05-28 20:47:49 +00:00
|
|
|
g.Fgenf(w, "%.[1]*[2]v %[3]v %.[1]*[4]o", precedence, expr.LeftOperand, opstr, expr.RightOperand)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (g *generator) GenConditionalExpression(w io.Writer, expr *model.ConditionalExpression) {
|
|
|
|
// Ternary expressions are not supported in go so we need to allocate temp variables in the parent scope.
|
|
|
|
// This is handled by lower expression and rewriteTernaries
|
|
|
|
contract.Failf("unlowered conditional expression @ %v", expr.SyntaxNode().Range())
|
|
|
|
}
|
2020-05-21 17:23:33 +00:00
|
|
|
|
|
|
|
// GenForExpression generates code for a ForExpression.
|
2022-04-18 09:03:42 +00:00
|
|
|
func (g *generator) GenForExpression(w io.Writer, expr *model.ForExpression) {
|
|
|
|
g.genNYI(w, "For expression")
|
|
|
|
}
|
|
|
|
|
[program-gen] Fix enum resolution from types of the form Union[string, Enum] and emit fully qualified enum cases (#15696)
# Description
This PR improves enum type resolution from strings. When we try to
resolve `Union[string, Enum]` for a string expression, we choose
`string` because it is the more general type since not every string is
assignable to `Enum`. However, here we spacial case strings that are
actually part of that `Enum`.
The result is that `pcl.LowerConversion` will choose `Enum` from
`Union[string, Enum]` when the value of the input string is compatible
with the enum. This greatly improves program-gen for all of typescript,
python, csharp and go which now will emit the fully qualified enum cases
instead of emitting strings.
Closes https://github.com/pulumi/pulumi-dotnet/issues/41 which is
supposed to be a duplicate of
https://github.com/pulumi/pulumi-azure-native/issues/2616 but that is
not the case (the former is about unions of objects, the latter is
unions of enums and strings)
## Checklist
- [ ] 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. -->
- [x] I have added tests that prove my fix is effective or that my
feature works
<!---
User-facing changes require a CHANGELOG entry.
-->
- [x] 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. -->
2024-03-15 17:49:12 +00:00
|
|
|
func (g *generator) genSafeEnum(w io.Writer, to *model.EnumType, dest model.Type) func(member *schema.Enum) {
|
2022-04-18 09:03:42 +00:00
|
|
|
return func(member *schema.Enum) {
|
|
|
|
// We know the enum value at the call site, so we can directly stamp in a
|
|
|
|
// valid enum instance. We don't need to convert.
|
|
|
|
enumName := tokenToName(to.Token)
|
|
|
|
memberTag := member.Name
|
|
|
|
if memberTag == "" {
|
|
|
|
memberTag = member.Value.(string)
|
|
|
|
}
|
|
|
|
memberTag, err := makeSafeEnumName(memberTag, enumName)
|
|
|
|
contract.AssertNoErrorf(err, "Enum is invalid")
|
2022-11-22 20:44:19 +00:00
|
|
|
pkg, mod, _, _ := pcl.DecomposeToken(to.Token, to.SyntaxNode().Range())
|
|
|
|
mod = g.getModOrAlias(pkg, mod, mod)
|
|
|
|
|
[program-gen] Fix enum resolution from types of the form Union[string, Enum] and emit fully qualified enum cases (#15696)
# Description
This PR improves enum type resolution from strings. When we try to
resolve `Union[string, Enum]` for a string expression, we choose
`string` because it is the more general type since not every string is
assignable to `Enum`. However, here we spacial case strings that are
actually part of that `Enum`.
The result is that `pcl.LowerConversion` will choose `Enum` from
`Union[string, Enum]` when the value of the input string is compatible
with the enum. This greatly improves program-gen for all of typescript,
python, csharp and go which now will emit the fully qualified enum cases
instead of emitting strings.
Closes https://github.com/pulumi/pulumi-dotnet/issues/41 which is
supposed to be a duplicate of
https://github.com/pulumi/pulumi-azure-native/issues/2616 but that is
not the case (the former is about unions of objects, the latter is
unions of enums and strings)
## Checklist
- [ ] 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. -->
- [x] I have added tests that prove my fix is effective or that my
feature works
<!---
User-facing changes require a CHANGELOG entry.
-->
- [x] 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. -->
2024-03-15 17:49:12 +00:00
|
|
|
if union, isUnion := dest.(*model.UnionType); isUnion && len(union.Annotations) > 0 {
|
|
|
|
if input, ok := union.Annotations[0].(schema.Type); ok {
|
|
|
|
if _, ok := codegen.ResolvedType(input).(*schema.UnionType); ok {
|
|
|
|
g.Fgenf(w, "pulumi.String(%s.%s)", mod, memberTag)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2022-11-22 20:44:19 +00:00
|
|
|
g.Fgenf(w, "%s.%s", mod, memberTag)
|
2022-04-18 09:03:42 +00:00
|
|
|
}
|
|
|
|
}
|
2020-05-21 17:23:33 +00:00
|
|
|
|
|
|
|
func (g *generator) GenFunctionCallExpression(w io.Writer, expr *model.FunctionCallExpression) {
|
|
|
|
switch expr.Name {
|
2021-09-30 03:11:56 +00:00
|
|
|
case pcl.IntrinsicConvert:
|
2022-04-18 09:03:42 +00:00
|
|
|
from := expr.Args[0]
|
|
|
|
to := pcl.LowerConversion(from, expr.Signature.ReturnType)
|
|
|
|
output, isOutput := to.(*model.OutputType)
|
2024-07-22 17:12:28 +00:00
|
|
|
originalTo := to
|
2022-04-18 09:03:42 +00:00
|
|
|
if isOutput {
|
|
|
|
to = output.ElementType
|
|
|
|
}
|
2024-07-22 17:12:28 +00:00
|
|
|
_, isFromOutput := from.Type().(*model.OutputType)
|
|
|
|
|
2022-04-18 09:03:42 +00:00
|
|
|
switch to := to.(type) {
|
|
|
|
case *model.EnumType:
|
|
|
|
var underlyingType string
|
|
|
|
switch {
|
|
|
|
case to.Type.Equals(model.StringType):
|
|
|
|
underlyingType = "string"
|
2024-05-30 22:43:12 +00:00
|
|
|
case to.Type.Equals(model.IntType):
|
|
|
|
underlyingType = "int"
|
2022-04-18 09:03:42 +00:00
|
|
|
default:
|
2024-05-30 22:43:12 +00:00
|
|
|
underlyingType = "float64"
|
2022-04-18 09:03:42 +00:00
|
|
|
}
|
2022-09-09 23:11:38 +00:00
|
|
|
pkg, mod, typ, _ := pcl.DecomposeToken(to.Token, to.SyntaxNode().Range())
|
|
|
|
mod = g.getModOrAlias(pkg, mod, mod)
|
|
|
|
enumTag := fmt.Sprintf("%s.%s", mod, typ)
|
2022-04-18 09:03:42 +00:00
|
|
|
if isOutput {
|
|
|
|
g.Fgenf(w,
|
|
|
|
"%.v.ApplyT(func(x *%[3]s) %[2]s { return %[2]s(*x) }).(%[2]sOutput)",
|
|
|
|
from, enumTag, underlyingType)
|
|
|
|
return
|
|
|
|
}
|
[program-gen] Fix enum resolution from types of the form Union[string, Enum] and emit fully qualified enum cases (#15696)
# Description
This PR improves enum type resolution from strings. When we try to
resolve `Union[string, Enum]` for a string expression, we choose
`string` because it is the more general type since not every string is
assignable to `Enum`. However, here we spacial case strings that are
actually part of that `Enum`.
The result is that `pcl.LowerConversion` will choose `Enum` from
`Union[string, Enum]` when the value of the input string is compatible
with the enum. This greatly improves program-gen for all of typescript,
python, csharp and go which now will emit the fully qualified enum cases
instead of emitting strings.
Closes https://github.com/pulumi/pulumi-dotnet/issues/41 which is
supposed to be a duplicate of
https://github.com/pulumi/pulumi-azure-native/issues/2616 but that is
not the case (the former is about unions of objects, the latter is
unions of enums and strings)
## Checklist
- [ ] 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. -->
- [x] I have added tests that prove my fix is effective or that my
feature works
<!---
User-facing changes require a CHANGELOG entry.
-->
- [x] 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. -->
2024-03-15 17:49:12 +00:00
|
|
|
diag := pcl.GenEnum(to, from, g.genSafeEnum(w, to, expr.Signature.ReturnType), func(from model.Expression) {
|
2022-04-18 09:03:42 +00:00
|
|
|
g.Fgenf(w, "%s(%v)", enumTag, from)
|
|
|
|
})
|
2022-12-01 22:41:24 +00:00
|
|
|
if diag != nil {
|
|
|
|
g.diagnostics = append(g.diagnostics, diag)
|
|
|
|
}
|
2022-04-18 09:03:42 +00:00
|
|
|
return
|
|
|
|
}
|
|
|
|
switch arg := from.(type) {
|
2020-05-21 17:23:33 +00:00
|
|
|
case *model.TupleConsExpression:
|
|
|
|
g.genTupleConsExpression(w, arg, expr.Type())
|
|
|
|
case *model.ObjectConsExpression:
|
2020-06-18 20:34:22 +00:00
|
|
|
isInput := false
|
|
|
|
g.genObjectConsExpression(w, arg, expr.Type(), isInput)
|
2020-06-06 01:52:00 +00:00
|
|
|
case *model.LiteralValueExpression:
|
|
|
|
g.genLiteralValueExpression(w, arg, expr.Type())
|
2021-06-24 16:17:55 +00:00
|
|
|
case *model.TemplateExpression:
|
|
|
|
g.genTemplateExpression(w, arg, expr.Type())
|
|
|
|
case *model.ScopeTraversalExpression:
|
|
|
|
g.genScopeTraversalExpression(w, arg, expr.Type())
|
2020-05-21 17:23:33 +00:00
|
|
|
default:
|
2024-07-22 17:12:28 +00:00
|
|
|
// Add a cast to the type we expect if needed
|
|
|
|
if originalTo.AssignableFrom(from.Type()) && (isOutput == isFromOutput) {
|
|
|
|
g.Fgenf(w, "%.v", from)
|
|
|
|
} else {
|
2024-07-23 07:03:26 +00:00
|
|
|
typeName := g.argumentTypeName(to, isOutput)
|
2024-07-22 17:12:28 +00:00
|
|
|
// IDOutput has a special case where it can be converted to a string
|
|
|
|
var isID bool
|
|
|
|
switch expr := from.(type) {
|
|
|
|
case *model.ScopeTraversalExpression:
|
|
|
|
last := expr.Traversal[len(expr.Traversal)-1]
|
|
|
|
if attr, ok := last.(hcl.TraverseAttr); ok && attr.Name == "id" {
|
|
|
|
isID = true
|
|
|
|
}
|
|
|
|
case *model.RelativeTraversalExpression:
|
|
|
|
last := expr.Traversal[len(expr.Traversal)-1]
|
|
|
|
if attr, ok := last.(hcl.TraverseAttr); ok && attr.Name == "id" {
|
|
|
|
isID = true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if typeName == "" {
|
|
|
|
g.Fgenf(w, "%.v", from)
|
|
|
|
} else if typeName == "pulumi.String" && isID {
|
|
|
|
g.Fgenf(w, "%.v", from)
|
|
|
|
} else {
|
|
|
|
g.Fgenf(w, "%s(%.v)", typeName, from)
|
|
|
|
}
|
|
|
|
}
|
2020-05-21 17:23:33 +00:00
|
|
|
}
|
2021-09-30 03:11:56 +00:00
|
|
|
case pcl.IntrinsicApply:
|
2020-05-26 14:07:54 +00:00
|
|
|
g.genApply(w, expr)
|
2020-05-21 17:23:33 +00:00
|
|
|
case "fileArchive":
|
2022-01-21 14:03:25 +00:00
|
|
|
g.Fgenf(w, "pulumi.NewFileArchive(%.v)", expr.Args[0])
|
2022-04-25 19:59:30 +00:00
|
|
|
case "remoteArchive":
|
|
|
|
g.Fgenf(w, "pulumi.NewRemoteArchive(%.v)", expr.Args[0])
|
|
|
|
case "assetArchive":
|
|
|
|
g.Fgenf(w, "pulumi.NewAssetArchive(%.v)", expr.Args[0])
|
2020-05-21 17:23:33 +00:00
|
|
|
case "fileAsset":
|
2020-06-16 06:00:02 +00:00
|
|
|
g.Fgenf(w, "pulumi.NewFileAsset(%.v)", expr.Args[0])
|
2022-04-25 19:59:30 +00:00
|
|
|
case "stringAsset":
|
|
|
|
g.Fgenf(w, "pulumi.NewStringAsset(%.v)", expr.Args[0])
|
|
|
|
case "remoteAsset":
|
|
|
|
g.Fgenf(w, "pulumi.NewRemoteAsset(%.v)", expr.Args[0])
|
2021-09-10 22:09:28 +00:00
|
|
|
case "filebase64":
|
|
|
|
// Assuming the existence of the following helper method
|
|
|
|
g.Fgenf(w, "filebase64OrPanic(%v)", expr.Args[0])
|
2022-01-21 14:03:25 +00:00
|
|
|
case "filebase64sha256":
|
|
|
|
// Assuming the existence of the following helper method
|
|
|
|
g.Fgenf(w, "filebase64sha256OrPanic(%v)", expr.Args[0])
|
2023-03-10 11:14:28 +00:00
|
|
|
case "notImplemented":
|
|
|
|
g.Fgenf(w, "notImplemented(%v)", expr.Args[0])
|
2023-06-11 09:05:10 +00:00
|
|
|
case "singleOrNone":
|
|
|
|
g.Fgenf(w, "singleOrNone(%v)", expr.Args[0])
|
2021-09-30 03:11:56 +00:00
|
|
|
case pcl.Invoke:
|
2023-01-11 22:17:14 +00:00
|
|
|
if expr.Signature.MultiArgumentInputs {
|
|
|
|
panic(fmt.Errorf("go program-gen does not implement MultiArgumentInputs for function '%v'",
|
|
|
|
expr.Args[0]))
|
|
|
|
}
|
2022-08-12 18:04:21 +00:00
|
|
|
|
2020-06-18 20:34:22 +00:00
|
|
|
pkg, module, fn, diags := g.functionName(expr.Args[0])
|
2022-08-12 18:04:21 +00:00
|
|
|
contract.Assertf(len(diags) == 0, "We don't allow problems getting the function name")
|
2023-07-10 13:05:18 +00:00
|
|
|
if module == "" || module == "index" {
|
2020-06-18 00:19:39 +00:00
|
|
|
module = pkg
|
|
|
|
}
|
2021-11-18 22:50:51 +00:00
|
|
|
isOut, outArgs, outArgsType := pcl.RecognizeOutputVersionedInvoke(expr)
|
|
|
|
if isOut {
|
2022-08-12 18:04:21 +00:00
|
|
|
outTypeName, err := outputVersionFunctionArgTypeName(outArgsType, g.externalCache)
|
2021-11-18 22:50:51 +00:00
|
|
|
if err != nil {
|
2022-08-12 18:04:21 +00:00
|
|
|
// We create a diag instead of panicking since panics are caught in go
|
|
|
|
// format expressions.
|
|
|
|
g.diagnostics = append(g.diagnostics, &hcl.Diagnostic{
|
|
|
|
Severity: hcl.DiagError,
|
|
|
|
Summary: "Error when generating an output-versioned Invoke",
|
|
|
|
Detail: fmt.Sprintf("underlying error: %v", err),
|
|
|
|
Subject: &hcl.Range{},
|
|
|
|
Context: &hcl.Range{},
|
|
|
|
Expression: nil,
|
|
|
|
EvalContext: &hcl.EvalContext{},
|
|
|
|
})
|
|
|
|
g.Fgenf(w, "%q", "failed") // Write a value to avoid syntax errors
|
|
|
|
return
|
2021-11-18 22:50:51 +00:00
|
|
|
}
|
|
|
|
g.Fgenf(w, "%s.%sOutput(ctx, ", module, fn)
|
|
|
|
g.genObjectConsExpressionWithTypeName(w, outArgs, outArgsType, outTypeName)
|
|
|
|
} else {
|
|
|
|
g.Fgenf(w, "%s.%s(ctx, ", module, fn)
|
|
|
|
g.Fgenf(w, "%.v", expr.Args[1])
|
|
|
|
}
|
2020-06-06 01:52:00 +00:00
|
|
|
|
Enable some more linting rules (#17456)
Issue #10659 lists a number of extra linting checks that we could enable
in order to make our Go code more robust. This commit implements as many
as seem sensible:
* `durationcheck`, which checks for multiplication of `time.Duration`s,
which can lead to unexpected behaviour (e.g. `time.Second * time.Second`
is *not* one second)
* `goprintffuncname`, which checks that `Printf`-like functions are
appropriately suffixed with `f` to indicate as such
* `tenv`, which checks for `os.Setenv` in tests where `t.Setenv` is
generally a better solution
* `wastedassign`, which checks for assignments whose values are never
used (such as initial values before an `if` where both branches then
overwrite the value)
* `whitespace`, which checks for blank lines at the beginning and end of
blocks such as functions, `if`s, `for`s and so on.
This commit does *not* enable the following checks listed in #10659:
* `wrapcheck`, which insists that third-party library errors are always
`%w`rapped -- we have a lot of cases where we don't do this and it's
probably a bit more involved than "just wrap them" in terms of making
sure we don't break anything (maybe)
* `predeclared`, which checks for shadowing of existing Go identifiers
-- we use `old` and `new` a lot, especially in step generation, so this
is probably a slightly bigger clean-up/one we might want to opt out of
* `mnd` (magic number detection) -- we have a lot of failures on this
* `nilnil` -- we only have a couple of failures on this; these could
probably be handled with `//nolint` but for now I've opted not to take
this route.
2024-10-03 17:37:13 +00:00
|
|
|
var optionsBag string
|
2020-06-06 01:52:00 +00:00
|
|
|
var buf bytes.Buffer
|
|
|
|
if len(expr.Args) == 3 {
|
2024-11-11 13:01:24 +00:00
|
|
|
if invokeOptions, ok := expr.Args[2].(*model.ObjectConsExpression); ok {
|
|
|
|
g.Fgen(&buf, ", ")
|
|
|
|
for i, item := range invokeOptions.Items {
|
|
|
|
last := i == len(invokeOptions.Items)-1
|
|
|
|
switch pcl.LiteralValueString(item.Key) {
|
|
|
|
case "provider":
|
|
|
|
g.Fgenf(&buf, "pulumi.Provider(%v)", item.Value)
|
|
|
|
case "parent":
|
|
|
|
g.Fgenf(&buf, "pulumi.Parent(%v)", item.Value)
|
|
|
|
case "version":
|
|
|
|
g.Fgenf(&buf, "pulumi.Version(%v)", item.Value)
|
|
|
|
case "pluginDownloadUrl":
|
|
|
|
g.Fgenf(&buf, "pulumi.PluginDownloadURL(%v)", item.Value)
|
|
|
|
}
|
|
|
|
|
|
|
|
if !last {
|
|
|
|
g.Fgen(&buf, ", ")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2020-06-06 01:52:00 +00:00
|
|
|
} else {
|
|
|
|
g.Fgenf(&buf, ", nil")
|
|
|
|
}
|
|
|
|
optionsBag = buf.String()
|
|
|
|
g.Fgenf(w, "%v)", optionsBag)
|
2021-07-29 03:41:23 +00:00
|
|
|
case "join":
|
|
|
|
g.Fgenf(w, "strings.Join(%v, %v)", expr.Args[1], expr.Args[0])
|
2020-05-21 17:23:33 +00:00
|
|
|
case "length":
|
2023-05-08 14:20:04 +00:00
|
|
|
g.Fgenf(w, "len(%.20v)", expr.Args[0])
|
2020-05-21 17:23:33 +00:00
|
|
|
case "readFile":
|
2021-09-09 19:10:25 +00:00
|
|
|
// Assuming the existence of the following helper method located earlier in the preamble
|
|
|
|
g.Fgenf(w, "readFileOrPanic(%v)", expr.Args[0])
|
2020-06-30 18:35:24 +00:00
|
|
|
case "secret":
|
|
|
|
outputTypeName := "pulumi.Any"
|
|
|
|
if model.ResolveOutputs(expr.Type()) != model.DynamicType {
|
2024-07-23 07:03:26 +00:00
|
|
|
outputTypeName = g.argumentTypeName(expr.Type(), false)
|
2020-06-30 18:35:24 +00:00
|
|
|
}
|
|
|
|
g.Fgenf(w, "pulumi.ToSecret(%v).(%sOutput)", expr.Args[0], outputTypeName)
|
2023-01-31 12:31:00 +00:00
|
|
|
case "unsecret":
|
|
|
|
outputTypeName := "pulumi.Any"
|
|
|
|
if model.ResolveOutputs(expr.Type()) != model.DynamicType {
|
2024-07-23 07:03:26 +00:00
|
|
|
outputTypeName = g.argumentTypeName(expr.Type(), false)
|
2023-01-31 12:31:00 +00:00
|
|
|
}
|
|
|
|
g.Fgenf(w, "pulumi.Unsecret(%v).(%sOutput)", expr.Args[0], outputTypeName)
|
2021-07-29 03:41:23 +00:00
|
|
|
case "toBase64":
|
|
|
|
g.Fgenf(w, "base64.StdEncoding.EncodeToString([]byte(%v))", expr.Args[0])
|
2022-09-16 23:12:29 +00:00
|
|
|
case fromBase64Fn:
|
|
|
|
g.Fgenf(w, "base64.StdEncoding.DecodeString(%v)", expr.Args[0])
|
2020-06-16 06:00:02 +00:00
|
|
|
case "mimeType":
|
|
|
|
g.Fgenf(w, "mime.TypeByExtension(path.Ext(%.v))", expr.Args[0])
|
2021-09-10 18:40:38 +00:00
|
|
|
case "sha1":
|
|
|
|
g.Fgenf(w, "sha1Hash(%v)", expr.Args[0])
|
[codegen/go] Improve optional params in invoke
As described in #8821, docs generated for helper functions can be incorrect because optional arguments to parameter objects are not correctly handled.
Previously, code would be generated like so:
```go
policyDocument, err := iam.GetPolicyDocument(ctx, &iam.GetPolicyDocumentArgs{
Statements: []iam.GetPolicyDocumentStatement{
iam.GetPolicyDocumentStatement{
Sid: "1",
//...
```
However "Sid" is of type `*string`.
This four helper conversion functions, to handle the primitive types we lower from, and modifies codegen to recursively apply these functions inside of an invoke call.
In the new code generation, the above is instead rendered as:
```go
policyDocument, err := iam.GetPolicyDocument(ctx, &iam.GetPolicyDocumentArgs{
Statements: []iam.GetPolicyDocumentStatement{
iam.GetPolicyDocumentStatement{
Sid: pulumi.StringRef("1"),
//...
```
2022-02-01 03:18:15 +00:00
|
|
|
case "goOptionalFloat64":
|
|
|
|
g.Fgenf(w, "pulumi.Float64Ref(%.v)", expr.Args[0])
|
|
|
|
case "goOptionalBool":
|
|
|
|
g.Fgenf(w, "pulumi.BoolRef(%.v)", expr.Args[0])
|
|
|
|
case "goOptionalInt":
|
|
|
|
g.Fgenf(w, "pulumi.IntRef(%.v)", expr.Args[0])
|
|
|
|
case "goOptionalString":
|
|
|
|
g.Fgenf(w, "pulumi.StringRef(%.v)", expr.Args[0])
|
2022-03-16 00:05:36 +00:00
|
|
|
case "stack":
|
|
|
|
g.Fgen(w, "ctx.Stack()")
|
|
|
|
case "project":
|
|
|
|
g.Fgen(w, "ctx.Project()")
|
2024-08-19 03:58:19 +00:00
|
|
|
case "organization":
|
|
|
|
g.Fgen(w, "ctx.Organization()")
|
2022-03-16 00:05:36 +00:00
|
|
|
case "cwd":
|
|
|
|
g.Fgen(w, "func(cwd string, err error) string { if err != nil { panic(err) }; return cwd }(os.Getwd())")
|
2024-07-30 08:12:38 +00:00
|
|
|
case "getOutput":
|
|
|
|
g.Fgenf(w, "%v.GetOutput(pulumi.String(%v))", expr.Args[0], expr.Args[1])
|
2020-05-21 17:23:33 +00:00
|
|
|
default:
|
2023-12-20 13:16:37 +00:00
|
|
|
// toJSON and readDir are reduced away, shouldn't see them here
|
|
|
|
reducedFunctions := codegen.NewStringSet("toJSON", "readDir")
|
|
|
|
contract.Assertf(!reducedFunctions.Has(expr.Name), "unlowered function %s", expr.Name)
|
|
|
|
// TODO: implement "element", "entries", "lookup", "split" and "range"
|
2020-05-21 17:23:33 +00:00
|
|
|
g.genNYI(w, "call %v", expr.Name)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-11-18 22:50:51 +00:00
|
|
|
// Currently args type for output-versioned invokes are named
|
|
|
|
// `FOutputArgs`, but this is not yet understood by `tokenToType`. Use
|
|
|
|
// this function to compensate.
|
2022-08-12 18:04:21 +00:00
|
|
|
func outputVersionFunctionArgTypeName(t model.Type, cache *Cache) (string, error) {
|
2021-11-18 22:50:51 +00:00
|
|
|
schemaType, ok := pcl.GetSchemaForType(t)
|
|
|
|
if !ok {
|
2023-12-12 12:19:42 +00:00
|
|
|
return "", errors.New("No schema.Type type found for the given model.Type")
|
2021-11-18 22:50:51 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
objType, ok := schemaType.(*schema.ObjectType)
|
|
|
|
if !ok {
|
|
|
|
return "", fmt.Errorf("Expected a schema.ObjectType, got %s", schemaType.String())
|
|
|
|
}
|
|
|
|
|
2022-12-08 10:45:46 +00:00
|
|
|
pkg := &pkgContext{
|
|
|
|
pkg: (&schema.Package{Name: "main"}).Reference(),
|
|
|
|
externalPackages: cache,
|
|
|
|
}
|
2021-11-18 22:50:51 +00:00
|
|
|
|
|
|
|
var ty string
|
|
|
|
if pkg.isExternalReference(objType) {
|
2022-07-06 18:35:31 +00:00
|
|
|
extPkg, _ := pkg.contextForExternalReference(objType)
|
|
|
|
ty = extPkg.tokenToType(objType.Token)
|
2021-11-18 22:50:51 +00:00
|
|
|
} else {
|
|
|
|
ty = pkg.tokenToType(objType.Token)
|
|
|
|
}
|
|
|
|
|
2023-12-12 12:19:42 +00:00
|
|
|
return strings.TrimSuffix(ty, "Args") + "OutputArgs", nil
|
2021-11-18 22:50:51 +00:00
|
|
|
}
|
|
|
|
|
2020-06-18 00:19:39 +00:00
|
|
|
func (g *generator) GenIndexExpression(w io.Writer, expr *model.IndexExpression) {
|
|
|
|
g.Fgenf(w, "%.20v[%.v]", expr.Collection, expr.Key)
|
|
|
|
}
|
2020-05-21 17:23:33 +00:00
|
|
|
|
|
|
|
func (g *generator) GenLiteralValueExpression(w io.Writer, expr *model.LiteralValueExpression) {
|
2020-06-06 01:52:00 +00:00
|
|
|
g.genLiteralValueExpression(w, expr, expr.Type())
|
|
|
|
}
|
|
|
|
|
|
|
|
func (g *generator) genLiteralValueExpression(w io.Writer, expr *model.LiteralValueExpression, destType model.Type) {
|
2021-06-24 16:17:55 +00:00
|
|
|
exprType := expr.Type()
|
|
|
|
if cns, ok := exprType.(*model.ConstType); ok {
|
|
|
|
exprType = cns.Type
|
|
|
|
}
|
|
|
|
|
|
|
|
if exprType == model.NoneType {
|
2020-08-07 02:09:18 +00:00
|
|
|
g.Fgen(w, "nil")
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2024-07-23 07:03:26 +00:00
|
|
|
argTypeName := g.argumentTypeName(destType, false)
|
2020-06-06 01:52:00 +00:00
|
|
|
isPulumiType := strings.HasPrefix(argTypeName, "pulumi.")
|
|
|
|
|
2021-06-24 16:17:55 +00:00
|
|
|
switch exprType {
|
|
|
|
case model.BoolType:
|
|
|
|
if isPulumiType {
|
|
|
|
g.Fgenf(w, "%s(%v)", argTypeName, expr.Value.True())
|
|
|
|
} else {
|
|
|
|
g.Fgenf(w, "%v", expr.Value.True())
|
|
|
|
}
|
|
|
|
case model.NumberType, model.IntType:
|
|
|
|
bf := expr.Value.AsBigFloat()
|
|
|
|
if i, acc := bf.Int64(); acc == big.Exact {
|
2020-06-06 01:52:00 +00:00
|
|
|
if isPulumiType {
|
2021-06-24 16:17:55 +00:00
|
|
|
g.Fgenf(w, "%s(%d)", argTypeName, i)
|
2020-06-06 01:52:00 +00:00
|
|
|
} else {
|
2021-06-24 16:17:55 +00:00
|
|
|
g.Fgenf(w, "%d", i)
|
2020-06-06 01:52:00 +00:00
|
|
|
}
|
2021-06-24 16:17:55 +00:00
|
|
|
} else {
|
|
|
|
f, _ := bf.Float64()
|
2020-06-06 01:52:00 +00:00
|
|
|
if isPulumiType {
|
2021-06-24 16:17:55 +00:00
|
|
|
g.Fgenf(w, "%s(%g)", argTypeName, f)
|
2020-06-06 01:52:00 +00:00
|
|
|
} else {
|
2021-06-24 16:17:55 +00:00
|
|
|
g.Fgenf(w, "%g", f)
|
2020-06-06 01:52:00 +00:00
|
|
|
}
|
|
|
|
}
|
2021-06-24 16:17:55 +00:00
|
|
|
case model.StringType:
|
|
|
|
strVal := expr.Value.AsString()
|
|
|
|
if isPulumiType {
|
|
|
|
g.Fgenf(w, "%s(", argTypeName)
|
2023-06-22 17:43:06 +00:00
|
|
|
g.genStringLiteral(w, strVal, true /* allow raw */)
|
2021-06-24 16:17:55 +00:00
|
|
|
g.Fgenf(w, ")")
|
|
|
|
} else {
|
2023-06-22 17:43:06 +00:00
|
|
|
g.genStringLiteral(w, strVal, true /* allow raw */)
|
2020-05-21 17:23:33 +00:00
|
|
|
}
|
|
|
|
default:
|
2021-06-24 16:17:55 +00:00
|
|
|
contract.Failf("unexpected opaque type in GenLiteralValueExpression: %v (%v)", destType,
|
2020-05-21 17:23:33 +00:00
|
|
|
expr.SyntaxNode().Range())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (g *generator) GenObjectConsExpression(w io.Writer, expr *model.ObjectConsExpression) {
|
2023-05-08 14:20:04 +00:00
|
|
|
switch argType := expr.Type().(type) {
|
|
|
|
case *model.ObjectType:
|
|
|
|
if len(argType.Annotations) > 0 {
|
|
|
|
if configMetadata, ok := argType.Annotations[0].(*ObjectTypeFromConfigMetadata); ok {
|
|
|
|
g.genObjectConsExpressionWithTypeName(w, expr, expr.Type(), configMetadata.TypeName)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-06-18 20:34:22 +00:00
|
|
|
isInput := false
|
|
|
|
g.genObjectConsExpression(w, expr, expr.Type(), isInput)
|
2020-05-21 17:23:33 +00:00
|
|
|
}
|
|
|
|
|
2020-06-18 20:34:22 +00:00
|
|
|
func (g *generator) genObjectConsExpression(
|
|
|
|
w io.Writer,
|
|
|
|
expr *model.ObjectConsExpression,
|
|
|
|
destType model.Type,
|
2023-03-03 16:36:39 +00:00
|
|
|
isInput bool,
|
|
|
|
) {
|
2021-06-24 16:17:55 +00:00
|
|
|
isInput = isInput || isInputty(destType)
|
2021-11-18 22:50:51 +00:00
|
|
|
|
2024-07-23 07:03:26 +00:00
|
|
|
typeName := g.argumentTypeName(destType, isInput)
|
2021-09-30 03:11:56 +00:00
|
|
|
if schemaType, ok := pcl.GetSchemaForType(destType); ok {
|
2021-06-24 16:17:55 +00:00
|
|
|
if obj, ok := codegen.UnwrapType(schemaType).(*schema.ObjectType); ok {
|
|
|
|
if g.useLookupInvokeForm(obj.Token) {
|
|
|
|
typeName = strings.Replace(typeName, ".Get", ".Lookup", 1)
|
|
|
|
}
|
2020-06-06 01:52:00 +00:00
|
|
|
}
|
2021-06-24 16:17:55 +00:00
|
|
|
}
|
2020-06-06 01:52:00 +00:00
|
|
|
|
2024-03-24 00:06:57 +00:00
|
|
|
if schemaType, ok := g.toSchemaType(destType); ok {
|
|
|
|
if codegen.ResolvedType(schemaType) == schema.AnyType {
|
|
|
|
g.Fgenf(w, "pulumi.Any(")
|
|
|
|
g.genObjectConsExpressionWithTypeName(w, expr, destType, "map[string]interface{}")
|
|
|
|
g.Fgenf(w, ")")
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-11-18 22:50:51 +00:00
|
|
|
g.genObjectConsExpressionWithTypeName(w, expr, destType, typeName)
|
|
|
|
}
|
|
|
|
|
2024-03-24 00:06:57 +00:00
|
|
|
func (g *generator) toSchemaType(destType model.Type) (schema.Type, bool) {
|
|
|
|
schemaType, ok := pcl.GetSchemaForType(destType)
|
|
|
|
if !ok {
|
|
|
|
return nil, false
|
|
|
|
}
|
|
|
|
return codegen.UnwrapType(schemaType), true
|
|
|
|
}
|
|
|
|
|
2021-11-18 22:50:51 +00:00
|
|
|
func (g *generator) genObjectConsExpressionWithTypeName(
|
|
|
|
w io.Writer,
|
|
|
|
expr *model.ObjectConsExpression,
|
|
|
|
destType model.Type,
|
2023-03-03 16:36:39 +00:00
|
|
|
typeName string,
|
|
|
|
) {
|
2021-06-24 16:17:55 +00:00
|
|
|
// TODO: @pgavlin --- ineffectual assignment, was there some work in flight here?
|
|
|
|
// if strings.HasSuffix(typeName, "Args") {
|
|
|
|
// isInput = true
|
|
|
|
// }
|
|
|
|
// // invokes are not inputty
|
|
|
|
// if strings.Contains(typeName, ".Lookup") || strings.Contains(typeName, ".Get") {
|
|
|
|
// isInput = false
|
|
|
|
// }
|
|
|
|
isMap := strings.HasPrefix(typeName, "map[")
|
|
|
|
|
|
|
|
// TODO: retrieve schema and propagate optionals to emit bool ptr, etc.
|
|
|
|
|
2022-12-15 22:45:07 +00:00
|
|
|
if g.inGenTupleConExprListArgs {
|
|
|
|
if g.isPtrArg {
|
|
|
|
g.Fgenf(w, "&%s", typeName)
|
|
|
|
}
|
|
|
|
} else if isMap || !strings.HasSuffix(typeName, "Args") || strings.HasSuffix(typeName, "OutputArgs") {
|
2021-06-24 16:17:55 +00:00
|
|
|
g.Fgenf(w, "%s", typeName)
|
|
|
|
} else {
|
|
|
|
g.Fgenf(w, "&%s", typeName)
|
|
|
|
}
|
|
|
|
g.Fgenf(w, "{\n")
|
|
|
|
|
|
|
|
for _, item := range expr.Items {
|
|
|
|
if lit, ok := g.literalKey(item.Key); ok {
|
|
|
|
if isMap || strings.HasSuffix(typeName, "Map") {
|
|
|
|
g.Fgenf(w, "\"%s\"", lit)
|
2020-06-06 01:52:00 +00:00
|
|
|
} else {
|
2021-06-24 16:17:55 +00:00
|
|
|
g.Fgenf(w, "%s", Title(lit))
|
2020-05-21 17:23:33 +00:00
|
|
|
}
|
2021-06-24 16:17:55 +00:00
|
|
|
} else {
|
|
|
|
g.Fgenf(w, "%.v", item.Key)
|
2020-05-21 17:23:33 +00:00
|
|
|
}
|
2020-06-06 01:52:00 +00:00
|
|
|
|
2021-06-24 16:17:55 +00:00
|
|
|
g.Fgenf(w, ": %.v,\n", item.Value)
|
2020-06-18 00:19:39 +00:00
|
|
|
}
|
2021-06-24 16:17:55 +00:00
|
|
|
|
|
|
|
g.Fgenf(w, "}")
|
2020-06-18 00:19:39 +00:00
|
|
|
}
|
|
|
|
|
2020-05-21 17:23:33 +00:00
|
|
|
func (g *generator) GenRelativeTraversalExpression(w io.Writer, expr *model.RelativeTraversalExpression) {
|
|
|
|
g.Fgenf(w, "%.20v", expr.Source)
|
2020-06-18 00:19:39 +00:00
|
|
|
isRootResource := false
|
|
|
|
if ie, ok := expr.Source.(*model.IndexExpression); ok {
|
|
|
|
if se, ok := ie.Collection.(*model.ScopeTraversalExpression); ok {
|
2021-09-30 03:11:56 +00:00
|
|
|
if _, ok := se.Parts[0].(*pcl.Resource); ok {
|
2020-06-18 00:19:39 +00:00
|
|
|
isRootResource = true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
g.genRelativeTraversal(w, expr.Traversal, expr.Parts, isRootResource)
|
2020-05-21 17:23:33 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (g *generator) GenScopeTraversalExpression(w io.Writer, expr *model.ScopeTraversalExpression) {
|
2021-06-24 16:17:55 +00:00
|
|
|
g.genScopeTraversalExpression(w, expr, expr.Type())
|
2020-06-06 01:52:00 +00:00
|
|
|
}
|
|
|
|
|
2021-06-24 16:17:55 +00:00
|
|
|
func (g *generator) genScopeTraversalExpression(
|
2023-03-03 16:36:39 +00:00
|
|
|
w io.Writer, expr *model.ScopeTraversalExpression, destType model.Type,
|
|
|
|
) {
|
2020-05-21 17:23:33 +00:00
|
|
|
rootName := expr.RootName
|
2020-06-18 00:19:39 +00:00
|
|
|
|
|
|
|
if _, ok := expr.Parts[0].(*model.SplatVariable); ok {
|
|
|
|
rootName = "val0"
|
|
|
|
}
|
2020-05-21 17:23:33 +00:00
|
|
|
|
2020-06-06 01:52:00 +00:00
|
|
|
genIDCall := false
|
2020-05-21 17:23:33 +00:00
|
|
|
|
2021-06-24 16:17:55 +00:00
|
|
|
isInput := false
|
2021-09-30 03:11:56 +00:00
|
|
|
if schemaType, ok := pcl.GetSchemaForType(destType); ok {
|
2021-06-24 16:17:55 +00:00
|
|
|
_, isInput = schemaType.(*schema.InputType)
|
|
|
|
}
|
|
|
|
|
2022-11-29 00:14:07 +00:00
|
|
|
var sourceIsPlain bool
|
|
|
|
switch root := expr.Parts[0].(type) {
|
|
|
|
case *pcl.Resource:
|
2020-06-06 01:52:00 +00:00
|
|
|
isInput = false
|
2022-11-29 00:14:07 +00:00
|
|
|
if _, ok := pcl.GetSchemaForType(root.InputType); ok {
|
2020-06-06 01:52:00 +00:00
|
|
|
// convert .id into .ID()
|
|
|
|
last := expr.Traversal[len(expr.Traversal)-1]
|
|
|
|
if attr, ok := last.(hcl.TraverseAttr); ok && attr.Name == "id" {
|
|
|
|
genIDCall = true
|
|
|
|
expr.Traversal = expr.Traversal[:len(expr.Traversal)-1]
|
|
|
|
}
|
2020-05-21 17:23:33 +00:00
|
|
|
}
|
2022-11-29 00:14:07 +00:00
|
|
|
case *pcl.LocalVariable:
|
|
|
|
if root, ok := root.Definition.Value.(*model.FunctionCallExpression); ok && !pcl.IsOutputVersionInvokeCall(root) {
|
|
|
|
sourceIsPlain = true
|
|
|
|
}
|
2023-05-08 14:20:04 +00:00
|
|
|
case *pcl.ConfigVariable:
|
|
|
|
if g.isComponent {
|
|
|
|
// config variables of components are always of type Input<T>
|
|
|
|
// these shouldn't be wrapped in a pulumi.String(...), pulumi.Int(...) etc. functions
|
|
|
|
g.Fgenf(w, "args.%s", Title(rootName))
|
|
|
|
isRootResource := false
|
|
|
|
g.genRelativeTraversal(w, expr.Traversal.SimpleSplit().Rel, expr.Parts[1:], isRootResource)
|
|
|
|
return
|
|
|
|
}
|
2020-05-21 17:23:33 +00:00
|
|
|
}
|
2020-06-06 01:52:00 +00:00
|
|
|
|
|
|
|
// TODO if it's an array type, we need a lowering step to turn []string -> pulumi.StringArray
|
|
|
|
if isInput {
|
2024-07-23 07:03:26 +00:00
|
|
|
argTypeName := g.argumentTypeName(expr.Type(), isInput)
|
2022-11-29 00:14:07 +00:00
|
|
|
if strings.HasSuffix(argTypeName, "Array") {
|
2024-07-23 07:03:26 +00:00
|
|
|
destTypeName := g.argumentTypeName(destType, isInput)
|
2022-11-29 00:14:07 +00:00
|
|
|
// `argTypeName` == `destTypeName` and `argTypeName` ends with `Array`, we
|
|
|
|
// know that `destType` is an outputty type. If the source is plain (and thus
|
|
|
|
// not outputty), then the types can never line up and we will need a
|
|
|
|
// conversion helper method.
|
|
|
|
if argTypeName != destTypeName || sourceIsPlain {
|
2021-06-24 16:17:55 +00:00
|
|
|
// use a helper to transform prompt arrays into inputty arrays
|
|
|
|
var helper *promptToInputArrayHelper
|
2022-11-29 00:14:07 +00:00
|
|
|
if h, ok := g.arrayHelpers[argTypeName]; ok {
|
2021-06-24 16:17:55 +00:00
|
|
|
helper = h
|
|
|
|
} else {
|
|
|
|
// helpers are emitted at the end in the postamble step
|
|
|
|
helper = &promptToInputArrayHelper{
|
2022-11-29 00:14:07 +00:00
|
|
|
destType: argTypeName,
|
2021-06-24 16:17:55 +00:00
|
|
|
}
|
2022-11-29 00:14:07 +00:00
|
|
|
g.arrayHelpers[argTypeName] = helper
|
2020-06-24 18:07:26 +00:00
|
|
|
}
|
2022-11-29 00:14:07 +00:00
|
|
|
// Wrap the emitted expression in a call to the generated helper function.
|
2021-06-24 16:17:55 +00:00
|
|
|
g.Fgenf(w, "%s(", helper.getFnName())
|
|
|
|
defer g.Fgenf(w, ")")
|
2020-06-24 18:07:26 +00:00
|
|
|
}
|
|
|
|
} else {
|
2022-11-29 00:14:07 +00:00
|
|
|
// Wrap the emitted expression in a type conversion.
|
2024-07-23 07:03:26 +00:00
|
|
|
g.Fgenf(w, "%s(", g.argumentTypeName(expr.Type(), isInput))
|
2021-06-24 16:17:55 +00:00
|
|
|
defer g.Fgenf(w, ")")
|
2020-06-24 18:07:26 +00:00
|
|
|
}
|
2020-06-06 01:52:00 +00:00
|
|
|
}
|
2020-06-16 06:00:02 +00:00
|
|
|
|
2020-06-18 00:19:39 +00:00
|
|
|
// TODO: this isn't exhaustively correct as "range" could be a legit var name
|
|
|
|
// instead we should probably use a fn call expression here for entries/range
|
|
|
|
// similar to other languages
|
2020-06-30 01:10:34 +00:00
|
|
|
if rootName == keywordRange {
|
2020-06-16 06:00:02 +00:00
|
|
|
part := expr.Traversal[1].(hcl.TraverseAttr).Name
|
|
|
|
switch part {
|
|
|
|
case "value":
|
|
|
|
g.Fgenf(w, "val0")
|
|
|
|
case "key":
|
|
|
|
g.Fgenf(w, "key0")
|
|
|
|
default:
|
|
|
|
contract.Failf("unexpected traversal on range expression: %s", part)
|
|
|
|
}
|
|
|
|
} else {
|
2020-06-30 01:10:34 +00:00
|
|
|
g.Fgen(w, makeValidIdentifier(rootName))
|
2020-06-18 00:19:39 +00:00
|
|
|
isRootResource := false
|
|
|
|
g.genRelativeTraversal(w, expr.Traversal.SimpleSplit().Rel, expr.Parts[1:], isRootResource)
|
2020-06-16 06:00:02 +00:00
|
|
|
}
|
2020-06-06 01:52:00 +00:00
|
|
|
|
|
|
|
if genIDCall {
|
|
|
|
g.Fgenf(w, ".ID()")
|
|
|
|
}
|
2020-05-21 17:23:33 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// GenSplatExpression generates code for a SplatExpression.
|
2020-06-18 00:19:39 +00:00
|
|
|
func (g *generator) GenSplatExpression(w io.Writer, expr *model.SplatExpression) {
|
|
|
|
contract.Failf("unlowered splat expression @ %v", expr.SyntaxNode().Range())
|
|
|
|
}
|
2020-05-21 17:23:33 +00:00
|
|
|
|
|
|
|
// GenTemplateExpression generates code for a TemplateExpression.
|
2020-05-27 14:32:13 +00:00
|
|
|
func (g *generator) GenTemplateExpression(w io.Writer, expr *model.TemplateExpression) {
|
2021-06-24 16:17:55 +00:00
|
|
|
g.genTemplateExpression(w, expr, expr.Type())
|
|
|
|
}
|
|
|
|
|
|
|
|
func (g *generator) genTemplateExpression(w io.Writer, expr *model.TemplateExpression, destType model.Type) {
|
2020-05-27 14:32:13 +00:00
|
|
|
if len(expr.Parts) == 1 {
|
2021-06-24 16:17:55 +00:00
|
|
|
if lit, ok := expr.Parts[0].(*model.LiteralValueExpression); ok && model.StringType.AssignableFrom(lit.Type()) {
|
|
|
|
g.genLiteralValueExpression(w, lit, destType)
|
|
|
|
}
|
|
|
|
|
2022-06-02 16:36:33 +00:00
|
|
|
// If we have a template expression that doesn't start with a string, it indicates
|
|
|
|
// an invalid *pcl.Program. Instead of crashing, we continue.
|
|
|
|
return
|
|
|
|
}
|
2024-07-23 07:03:26 +00:00
|
|
|
argTypeName := g.argumentTypeName(destType, false)
|
2022-06-02 16:36:33 +00:00
|
|
|
isPulumiType := strings.HasPrefix(argTypeName, "pulumi.")
|
2024-08-01 09:24:25 +00:00
|
|
|
isPulumiStr := argTypeName == "pulumi.String"
|
|
|
|
if isPulumiType && !isPulumiStr {
|
2022-06-02 16:36:33 +00:00
|
|
|
g.Fgenf(w, "%s(", argTypeName)
|
|
|
|
defer g.Fgenf(w, ")")
|
|
|
|
}
|
|
|
|
|
|
|
|
var fmtStr strings.Builder
|
|
|
|
args := new(bytes.Buffer)
|
|
|
|
canBeRaw := true
|
|
|
|
for _, v := range expr.Parts {
|
|
|
|
if lit, ok := v.(*model.LiteralValueExpression); ok && lit.Value.Type().Equals(cty.String) {
|
|
|
|
str := lit.Value.AsString()
|
|
|
|
// We don't want to accidentally embed a formatting directive in our
|
|
|
|
// formatting string.
|
|
|
|
if !strings.ContainsRune(str, '%') {
|
|
|
|
if canBeRaw && strings.ContainsRune(str, '`') {
|
|
|
|
canBeRaw = false
|
|
|
|
}
|
|
|
|
// Build the formatting string
|
|
|
|
fmtStr.WriteString(str)
|
|
|
|
continue
|
|
|
|
}
|
2020-06-16 06:00:02 +00:00
|
|
|
}
|
2022-06-02 16:36:33 +00:00
|
|
|
// v cannot be directly inserted into the formatting string, so put it in the
|
|
|
|
// argument list.
|
|
|
|
fmtStr.WriteString("%v")
|
|
|
|
g.Fgenf(args, ", %.v", v)
|
|
|
|
}
|
2024-08-01 09:24:25 +00:00
|
|
|
if isPulumiStr {
|
|
|
|
g.Fgenf(w, "pulumi.Sprintf(")
|
|
|
|
} else {
|
|
|
|
g.Fgenf(w, "fmt.Sprintf(")
|
|
|
|
}
|
2023-06-22 17:43:06 +00:00
|
|
|
g.genStringLiteral(w, fmtStr.String(), canBeRaw)
|
2022-06-02 16:36:33 +00:00
|
|
|
_, err := args.WriteTo(w)
|
2023-02-08 19:18:48 +00:00
|
|
|
contract.AssertNoErrorf(err, "Failed to write arguments")
|
2022-06-02 16:36:33 +00:00
|
|
|
g.Fgenf(w, ")")
|
2020-05-27 14:32:13 +00:00
|
|
|
}
|
2020-05-21 17:23:33 +00:00
|
|
|
|
|
|
|
// GenTemplateJoinExpression generates code for a TemplateJoinExpression.
|
|
|
|
func (g *generator) GenTemplateJoinExpression(w io.Writer, expr *model.TemplateJoinExpression) { /*TODO*/
|
|
|
|
}
|
|
|
|
|
|
|
|
func (g *generator) GenTupleConsExpression(w io.Writer, expr *model.TupleConsExpression) {
|
|
|
|
g.genTupleConsExpression(w, expr, expr.Type())
|
|
|
|
}
|
|
|
|
|
|
|
|
// GenTupleConsExpression generates code for a TupleConsExpression.
|
|
|
|
func (g *generator) genTupleConsExpression(w io.Writer, expr *model.TupleConsExpression, destType model.Type) {
|
2021-06-24 16:17:55 +00:00
|
|
|
isInput := isInputty(destType)
|
2024-07-23 07:03:26 +00:00
|
|
|
argType := g.argumentTypeName(destType, isInput)
|
2022-12-15 22:45:07 +00:00
|
|
|
// don't need to generate type for list args if not a pointer, i.e. []ec2.SubnetSpecArgs{ {Type: ...} }
|
|
|
|
// unless it contains an interface, i.e. []map[string]interface{ map[string]interface{"key": "val"} }
|
|
|
|
if strings.HasPrefix(argType, "[]") && !strings.Contains(argType, "interface{}") {
|
|
|
|
defer func(b bool) { g.inGenTupleConExprListArgs = b }(g.inGenTupleConExprListArgs)
|
|
|
|
g.inGenTupleConExprListArgs = true
|
|
|
|
if strings.HasPrefix(argType, "[]*") {
|
|
|
|
defer func(b bool) { g.isPtrArg = b }(g.isPtrArg)
|
|
|
|
g.isPtrArg = true
|
|
|
|
}
|
|
|
|
}
|
2020-05-21 17:23:33 +00:00
|
|
|
g.Fgenf(w, "%s{\n", argType)
|
2024-07-05 12:42:41 +00:00
|
|
|
for _, v := range expr.Expressions {
|
|
|
|
g.Fgenf(w, "%v,\n", v)
|
2020-05-21 17:23:33 +00:00
|
|
|
}
|
|
|
|
g.Fgenf(w, "}")
|
|
|
|
}
|
|
|
|
|
2020-05-28 20:47:49 +00:00
|
|
|
func (g *generator) GenUnaryOpExpression(w io.Writer, expr *model.UnaryOpExpression) {
|
Enable some more linting rules (#17456)
Issue #10659 lists a number of extra linting checks that we could enable
in order to make our Go code more robust. This commit implements as many
as seem sensible:
* `durationcheck`, which checks for multiplication of `time.Duration`s,
which can lead to unexpected behaviour (e.g. `time.Second * time.Second`
is *not* one second)
* `goprintffuncname`, which checks that `Printf`-like functions are
appropriately suffixed with `f` to indicate as such
* `tenv`, which checks for `os.Setenv` in tests where `t.Setenv` is
generally a better solution
* `wastedassign`, which checks for assignments whose values are never
used (such as initial values before an `if` where both branches then
overwrite the value)
* `whitespace`, which checks for blank lines at the beginning and end of
blocks such as functions, `if`s, `for`s and so on.
This commit does *not* enable the following checks listed in #10659:
* `wrapcheck`, which insists that third-party library errors are always
`%w`rapped -- we have a lot of cases where we don't do this and it's
probably a bit more involved than "just wrap them" in terms of making
sure we don't break anything (maybe)
* `predeclared`, which checks for shadowing of existing Go identifiers
-- we use `old` and `new` a lot, especially in step generation, so this
is probably a slightly bigger clean-up/one we might want to opt out of
* `mnd` (magic number detection) -- we have a lot of failures on this
* `nilnil` -- we only have a couple of failures on this; these could
probably be handled with `//nolint` but for now I've opted not to take
this route.
2024-10-03 17:37:13 +00:00
|
|
|
var opstr string
|
|
|
|
precedence := g.GetPrecedence(expr)
|
2020-05-28 20:47:49 +00:00
|
|
|
switch expr.Operation {
|
|
|
|
case hclsyntax.OpLogicalNot:
|
|
|
|
opstr = "!"
|
|
|
|
case hclsyntax.OpNegate:
|
|
|
|
opstr = "-"
|
|
|
|
}
|
|
|
|
g.Fgenf(w, "%[2]v%.[1]*[3]v", precedence, opstr, expr.Operand)
|
|
|
|
}
|
2020-05-21 17:23:33 +00:00
|
|
|
|
2024-07-23 07:03:26 +00:00
|
|
|
// argumentTypeName computes the go type for the given model type.
|
|
|
|
func (g *generator) argumentTypeName(destType model.Type, isInput bool) (result string) {
|
2021-06-24 16:17:55 +00:00
|
|
|
if cns, ok := destType.(*model.ConstType); ok {
|
|
|
|
destType = cns.Type
|
|
|
|
}
|
|
|
|
|
|
|
|
// This can happen with null literals.
|
|
|
|
if destType == model.NoneType {
|
|
|
|
return ""
|
|
|
|
}
|
|
|
|
|
2021-09-30 03:11:56 +00:00
|
|
|
if schemaType, ok := pcl.GetSchemaForType(destType); ok {
|
2022-12-08 10:45:46 +00:00
|
|
|
return (&pkgContext{
|
|
|
|
pkg: (&schema.Package{Name: "main"}).Reference(),
|
|
|
|
externalPackages: g.externalCache,
|
|
|
|
}).argsType(schemaType)
|
2020-05-21 17:23:33 +00:00
|
|
|
}
|
|
|
|
|
2020-05-28 20:47:49 +00:00
|
|
|
switch destType := destType.(type) {
|
2020-05-26 14:07:54 +00:00
|
|
|
case *model.OpaqueType:
|
2022-06-13 18:13:03 +00:00
|
|
|
switch *destType {
|
|
|
|
case *model.IntType:
|
2020-06-06 01:52:00 +00:00
|
|
|
if isInput {
|
|
|
|
return "pulumi.Int"
|
|
|
|
}
|
|
|
|
return "int"
|
2022-06-13 18:13:03 +00:00
|
|
|
case *model.NumberType:
|
2020-06-06 01:52:00 +00:00
|
|
|
if isInput {
|
|
|
|
return "pulumi.Float64"
|
|
|
|
}
|
2020-05-28 20:47:49 +00:00
|
|
|
return "float64"
|
2022-06-13 18:13:03 +00:00
|
|
|
case *model.StringType:
|
2020-06-06 01:52:00 +00:00
|
|
|
if isInput {
|
|
|
|
return "pulumi.String"
|
|
|
|
}
|
|
|
|
return "string"
|
2022-06-13 18:13:03 +00:00
|
|
|
case *model.BoolType:
|
2020-06-06 01:52:00 +00:00
|
|
|
if isInput {
|
|
|
|
return "pulumi.Bool"
|
|
|
|
}
|
|
|
|
return "bool"
|
2022-06-13 18:13:03 +00:00
|
|
|
case *model.DynamicType:
|
2020-06-30 18:35:24 +00:00
|
|
|
if isInput {
|
|
|
|
return "pulumi.Any"
|
|
|
|
}
|
|
|
|
return "interface{}"
|
2020-05-28 20:47:49 +00:00
|
|
|
default:
|
2022-06-13 18:13:03 +00:00
|
|
|
return string(*destType)
|
2020-05-28 20:47:49 +00:00
|
|
|
}
|
2020-06-06 01:52:00 +00:00
|
|
|
case *model.ObjectType:
|
2020-06-29 22:29:41 +00:00
|
|
|
|
2020-06-18 00:19:39 +00:00
|
|
|
if isInput {
|
2020-06-29 22:29:41 +00:00
|
|
|
// check for element type uniformity and return appropriate type if so
|
|
|
|
allSameType := true
|
|
|
|
var elmType string
|
|
|
|
for _, v := range destType.Properties {
|
2024-07-23 07:03:26 +00:00
|
|
|
valType := g.argumentTypeName(v, true)
|
2020-06-29 22:29:41 +00:00
|
|
|
if elmType != "" && elmType != valType {
|
|
|
|
allSameType = false
|
|
|
|
break
|
|
|
|
}
|
|
|
|
elmType = valType
|
|
|
|
}
|
|
|
|
if allSameType && elmType != "" {
|
2023-12-12 12:19:42 +00:00
|
|
|
return elmType + "Map"
|
2020-06-29 22:29:41 +00:00
|
|
|
}
|
2020-06-18 00:19:39 +00:00
|
|
|
return "pulumi.Map"
|
|
|
|
}
|
2020-06-06 01:52:00 +00:00
|
|
|
return "map[string]interface{}"
|
|
|
|
case *model.MapType:
|
2024-07-23 07:03:26 +00:00
|
|
|
valType := g.argumentTypeName(destType.ElementType, isInput)
|
2020-06-18 00:19:39 +00:00
|
|
|
if isInput {
|
2023-12-22 15:33:56 +00:00
|
|
|
trimmedType := strings.TrimPrefix(valType, "pulumi.")
|
|
|
|
return fmt.Sprintf("pulumi.%sMap", Title(trimmedType))
|
2020-06-18 00:19:39 +00:00
|
|
|
}
|
2023-12-12 12:19:42 +00:00
|
|
|
return "map[string]" + valType
|
2020-06-06 01:52:00 +00:00
|
|
|
case *model.ListType:
|
2024-07-23 07:03:26 +00:00
|
|
|
argTypeName := g.argumentTypeName(destType.ElementType, isInput)
|
2020-06-29 23:33:52 +00:00
|
|
|
if strings.HasPrefix(argTypeName, "pulumi.") && argTypeName != "pulumi.Resource" {
|
2023-10-20 16:12:25 +00:00
|
|
|
if argTypeName == "pulumi.Any" {
|
|
|
|
return "pulumi.Array"
|
|
|
|
}
|
2023-12-12 12:19:42 +00:00
|
|
|
return argTypeName + "Array"
|
2020-06-06 01:52:00 +00:00
|
|
|
}
|
2023-12-12 12:19:42 +00:00
|
|
|
return "[]" + argTypeName
|
2020-06-06 01:52:00 +00:00
|
|
|
case *model.TupleType:
|
|
|
|
// attempt to collapse tuple types. intentionally does not use model.UnifyTypes
|
|
|
|
// correct go code requires all types to match, or use of interface{}
|
|
|
|
var elmType model.Type
|
|
|
|
for i, t := range destType.ElementTypes {
|
|
|
|
if i == 0 {
|
|
|
|
elmType = t
|
2021-06-24 16:17:55 +00:00
|
|
|
if cns, ok := elmType.(*model.ConstType); ok {
|
|
|
|
elmType = cns.Type
|
|
|
|
}
|
|
|
|
continue
|
2020-06-06 01:52:00 +00:00
|
|
|
}
|
|
|
|
|
2021-06-24 16:17:55 +00:00
|
|
|
if !elmType.AssignableFrom(t) {
|
2020-06-06 01:52:00 +00:00
|
|
|
elmType = nil
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if elmType != nil {
|
2024-07-23 07:03:26 +00:00
|
|
|
argTypeName := g.argumentTypeName(elmType, isInput)
|
2020-06-29 23:33:52 +00:00
|
|
|
if strings.HasPrefix(argTypeName, "pulumi.") && argTypeName != "pulumi.Resource" {
|
2023-10-20 16:12:25 +00:00
|
|
|
if argTypeName == "pulumi.Any" {
|
|
|
|
return "pulumi.Array"
|
|
|
|
}
|
2023-12-12 12:19:42 +00:00
|
|
|
return argTypeName + "Array"
|
2020-06-06 01:52:00 +00:00
|
|
|
}
|
2023-12-12 12:19:42 +00:00
|
|
|
return "[]" + argTypeName
|
2020-06-06 01:52:00 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if isInput {
|
|
|
|
return "pulumi.Array"
|
|
|
|
}
|
|
|
|
return "[]interface{}"
|
|
|
|
case *model.OutputType:
|
|
|
|
isInput = true
|
2024-07-23 07:03:26 +00:00
|
|
|
return g.argumentTypeName(destType.ElementType, isInput)
|
2020-06-06 01:52:00 +00:00
|
|
|
case *model.UnionType:
|
|
|
|
for _, ut := range destType.ElementTypes {
|
2022-12-12 23:18:35 +00:00
|
|
|
isOptional := false
|
|
|
|
// check if the union contains none, which indicates this is an optional value
|
|
|
|
for _, ut := range destType.ElementTypes {
|
|
|
|
if ut.Equals(model.NoneType) {
|
|
|
|
isOptional = true
|
|
|
|
}
|
|
|
|
}
|
2021-06-24 16:17:55 +00:00
|
|
|
switch ut := ut.(type) {
|
|
|
|
case *model.OpaqueType:
|
2022-12-12 23:18:35 +00:00
|
|
|
if isOptional {
|
2024-07-23 07:03:26 +00:00
|
|
|
return g.argumentTypeNamePtr(ut, isInput)
|
2022-12-12 23:18:35 +00:00
|
|
|
}
|
2024-07-23 07:03:26 +00:00
|
|
|
return g.argumentTypeName(ut, isInput)
|
2021-06-24 16:17:55 +00:00
|
|
|
case *model.ConstType:
|
2024-07-23 07:03:26 +00:00
|
|
|
return g.argumentTypeName(ut.Type, isInput)
|
2021-06-24 16:17:55 +00:00
|
|
|
case *model.TupleType:
|
2024-07-23 07:03:26 +00:00
|
|
|
return g.argumentTypeName(ut, isInput)
|
2024-07-08 23:23:47 +00:00
|
|
|
case *model.MapType:
|
2024-07-23 07:03:26 +00:00
|
|
|
return g.argumentTypeName(ut, isInput)
|
2021-04-16 12:04:42 +00:00
|
|
|
}
|
2020-06-06 01:52:00 +00:00
|
|
|
}
|
2020-05-28 20:47:49 +00:00
|
|
|
return "interface{}"
|
2020-06-06 01:52:00 +00:00
|
|
|
case *model.PromiseType:
|
2024-07-23 07:03:26 +00:00
|
|
|
return g.argumentTypeName(destType.ElementType, isInput)
|
2020-05-26 14:07:54 +00:00
|
|
|
default:
|
2020-06-06 01:52:00 +00:00
|
|
|
contract.Failf("unexpected destType type %T", destType)
|
2020-05-26 14:07:54 +00:00
|
|
|
}
|
2020-05-21 17:23:33 +00:00
|
|
|
return ""
|
|
|
|
}
|
|
|
|
|
2024-07-23 07:03:26 +00:00
|
|
|
func (g *generator) argumentTypeNamePtr(destType model.Type, isInput bool) (result string) {
|
|
|
|
res := g.argumentTypeName(destType, isInput)
|
2024-03-10 04:49:06 +00:00
|
|
|
if !strings.HasPrefix(res, "pulumi.") {
|
|
|
|
return "*" + res
|
|
|
|
}
|
|
|
|
return res
|
2022-12-12 23:18:35 +00:00
|
|
|
}
|
|
|
|
|
2020-05-21 17:23:33 +00:00
|
|
|
func (g *generator) genRelativeTraversal(w io.Writer,
|
2023-03-03 16:36:39 +00:00
|
|
|
traversal hcl.Traversal, parts []model.Traversable, isRootResource bool,
|
|
|
|
) {
|
2020-06-18 00:19:39 +00:00
|
|
|
for i, part := range traversal {
|
2020-05-21 17:23:33 +00:00
|
|
|
var key cty.Value
|
|
|
|
switch part := part.(type) {
|
|
|
|
case hcl.TraverseAttr:
|
|
|
|
key = cty.StringVal(part.Name)
|
|
|
|
case hcl.TraverseIndex:
|
|
|
|
key = part.Key
|
|
|
|
default:
|
|
|
|
contract.Failf("unexpected traversal part of type %T (%v)", part, part.SourceRange())
|
|
|
|
}
|
|
|
|
|
|
|
|
// TODO handle optionals in go
|
|
|
|
// if model.IsOptionalType(model.GetTraversableType(parts[i])) {
|
|
|
|
// g.Fgen(w, "?")
|
|
|
|
// }
|
|
|
|
|
|
|
|
switch key.Type() {
|
|
|
|
case cty.String:
|
2020-06-18 00:19:39 +00:00
|
|
|
shouldConvert := isRootResource
|
|
|
|
if _, ok := parts[i].(*model.OutputType); ok {
|
|
|
|
shouldConvert = true
|
|
|
|
}
|
|
|
|
if key.AsString() == "id" && shouldConvert {
|
|
|
|
g.Fgenf(w, ".ID()")
|
|
|
|
} else {
|
|
|
|
g.Fgenf(w, ".%s", Title(key.AsString()))
|
|
|
|
}
|
2020-05-21 17:23:33 +00:00
|
|
|
case cty.Number:
|
|
|
|
idx, _ := key.AsBigFloat().Int64()
|
|
|
|
g.Fgenf(w, "[%d]", idx)
|
|
|
|
default:
|
|
|
|
contract.Failf("unexpected traversal key of type %T (%v)", key, key.AsString())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
type nameInfo int
|
|
|
|
|
|
|
|
func (nameInfo) Format(name string) string {
|
|
|
|
// TODO
|
|
|
|
return name
|
|
|
|
}
|
|
|
|
|
2020-06-18 00:19:39 +00:00
|
|
|
// lowerExpression amends the expression with intrinsics for Go generation.
|
2021-06-24 16:17:55 +00:00
|
|
|
func (g *generator) lowerExpression(expr model.Expression, typ model.Type) (
|
2023-03-03 16:36:39 +00:00
|
|
|
model.Expression, []interface{},
|
|
|
|
) {
|
2021-09-30 03:11:56 +00:00
|
|
|
expr = pcl.RewritePropertyReferences(expr)
|
|
|
|
expr, diags := pcl.RewriteApplies(expr, nameInfo(0), false /*TODO*/)
|
2023-06-07 11:22:41 +00:00
|
|
|
expr, sTemps, splatDiags := g.rewriteSplat(expr, g.splatSpiller)
|
2023-11-03 14:22:43 +00:00
|
|
|
|
2022-07-07 20:15:47 +00:00
|
|
|
expr, convertDiags := pcl.RewriteConversions(expr, typ)
|
2020-06-16 06:00:02 +00:00
|
|
|
expr, tTemps, ternDiags := g.rewriteTernaries(expr, g.ternaryTempSpiller)
|
2021-06-24 16:17:55 +00:00
|
|
|
expr, jTemps, jsonDiags := g.rewriteToJSON(expr)
|
2020-06-16 06:00:02 +00:00
|
|
|
expr, rTemps, readDirDiags := g.rewriteReadDir(expr, g.readDirTempSpiller)
|
2020-06-23 17:59:26 +00:00
|
|
|
expr, oTemps, optDiags := g.rewriteOptionals(expr, g.optionalSpiller)
|
2020-06-06 01:52:00 +00:00
|
|
|
|
2023-03-03 16:36:39 +00:00
|
|
|
bufferSize := len(tTemps) + len(jTemps) + len(rTemps) + len(sTemps) + len(oTemps)
|
2023-06-28 16:02:04 +00:00
|
|
|
temps := slice.Prealloc[interface{}](bufferSize)
|
2020-06-06 01:52:00 +00:00
|
|
|
for _, t := range tTemps {
|
|
|
|
temps = append(temps, t)
|
|
|
|
}
|
|
|
|
for _, t := range jTemps {
|
|
|
|
temps = append(temps, t)
|
|
|
|
}
|
2020-06-16 06:00:02 +00:00
|
|
|
for _, t := range rTemps {
|
|
|
|
temps = append(temps, t)
|
|
|
|
}
|
2020-06-18 00:19:39 +00:00
|
|
|
for _, t := range sTemps {
|
|
|
|
temps = append(temps, t)
|
|
|
|
}
|
2020-06-23 17:59:26 +00:00
|
|
|
for _, t := range oTemps {
|
|
|
|
temps = append(temps, t)
|
|
|
|
}
|
2022-07-07 20:15:47 +00:00
|
|
|
diags = append(diags, convertDiags...)
|
2020-05-28 20:47:49 +00:00
|
|
|
diags = append(diags, ternDiags...)
|
2020-06-06 01:52:00 +00:00
|
|
|
diags = append(diags, jsonDiags...)
|
2020-06-16 06:00:02 +00:00
|
|
|
diags = append(diags, readDirDiags...)
|
2020-06-18 00:19:39 +00:00
|
|
|
diags = append(diags, splatDiags...)
|
2020-06-23 17:59:26 +00:00
|
|
|
diags = append(diags, optDiags...)
|
2022-07-13 21:29:34 +00:00
|
|
|
g.diagnostics = g.diagnostics.Extend(diags)
|
2020-05-28 20:47:49 +00:00
|
|
|
return expr, temps
|
2020-05-21 17:23:33 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (g *generator) genNYI(w io.Writer, reason string, vs ...interface{}) {
|
2023-12-12 12:19:42 +00:00
|
|
|
message := "not yet implemented: " + fmt.Sprintf(reason, vs...)
|
2023-06-02 15:17:59 +00:00
|
|
|
g.diagnostics = append(g.diagnostics, &hcl.Diagnostic{
|
|
|
|
Severity: hcl.DiagWarning,
|
|
|
|
Summary: message,
|
|
|
|
Detail: message,
|
|
|
|
})
|
2020-05-21 17:23:33 +00:00
|
|
|
g.Fgenf(w, "\"TODO: %s\"", fmt.Sprintf(reason, vs...))
|
|
|
|
}
|
2020-05-26 14:07:54 +00:00
|
|
|
|
|
|
|
func (g *generator) genApply(w io.Writer, expr *model.FunctionCallExpression) {
|
|
|
|
// Extract the list of outputs and the continuation expression from the `__apply` arguments.
|
2021-09-30 03:11:56 +00:00
|
|
|
applyArgs, then := pcl.ParseApplyCall(expr)
|
2020-05-26 14:07:54 +00:00
|
|
|
isInput := false
|
2024-07-23 07:03:26 +00:00
|
|
|
retType := g.argumentTypeName(then.Signature.ReturnType, isInput)
|
2020-05-26 14:07:54 +00:00
|
|
|
// TODO account for outputs in other namespaces like aws
|
2021-11-18 22:50:51 +00:00
|
|
|
// TODO[pulumi/pulumi#8453] incomplete pattern code below.
|
|
|
|
var typeAssertion string
|
|
|
|
if retType == "[]string" {
|
|
|
|
typeAssertion = ".(pulumi.StringArrayOutput)"
|
|
|
|
} else {
|
2022-12-12 23:18:35 +00:00
|
|
|
if strings.HasPrefix(retType, "*") {
|
|
|
|
retType = Title(strings.TrimPrefix(retType, "*")) + "Ptr"
|
|
|
|
switch then.Body.(type) {
|
|
|
|
case *model.ScopeTraversalExpression:
|
|
|
|
traversal := then.Body.(*model.ScopeTraversalExpression)
|
|
|
|
traversal.RootName = "&" + traversal.RootName
|
|
|
|
then.Body = traversal
|
|
|
|
}
|
|
|
|
}
|
2021-11-18 22:50:51 +00:00
|
|
|
typeAssertion = fmt.Sprintf(".(%sOutput)", retType)
|
2022-05-20 17:29:05 +00:00
|
|
|
if !strings.Contains(retType, ".") {
|
2021-11-18 22:50:51 +00:00
|
|
|
typeAssertion = fmt.Sprintf(".(pulumi.%sOutput)", Title(retType))
|
|
|
|
}
|
2020-06-16 06:00:02 +00:00
|
|
|
}
|
2020-05-26 14:07:54 +00:00
|
|
|
|
|
|
|
if len(applyArgs) == 1 {
|
|
|
|
// If we only have a single output, just generate a normal `.Apply`
|
|
|
|
g.Fgenf(w, "%.v.ApplyT(%.v)%s", applyArgs[0], then, typeAssertion)
|
|
|
|
} else {
|
2020-06-18 00:19:39 +00:00
|
|
|
g.Fgenf(w, "pulumi.All(%.v", applyArgs[0])
|
|
|
|
applyArgs = applyArgs[1:]
|
|
|
|
for _, a := range applyArgs {
|
|
|
|
g.Fgenf(w, ",%.v", a)
|
|
|
|
}
|
2020-06-19 00:17:51 +00:00
|
|
|
allApplyThen, typeConvDecls := g.rewriteThenForAllApply(then)
|
|
|
|
g.Fgenf(w, ").ApplyT(")
|
2021-06-24 16:17:55 +00:00
|
|
|
g.genAnonymousFunctionExpression(w, allApplyThen, typeConvDecls, true)
|
2020-06-19 00:17:51 +00:00
|
|
|
g.Fgenf(w, ")%s", typeAssertion)
|
2020-05-26 14:07:54 +00:00
|
|
|
}
|
|
|
|
}
|
2020-05-27 14:32:13 +00:00
|
|
|
|
2020-06-19 00:17:51 +00:00
|
|
|
// rewriteThenForAllApply rewrites an apply func after a .All replacing params with []interface{}
|
|
|
|
// other languages like javascript take advantage of destructuring to simplify All.Apply
|
|
|
|
// by generating something like [a1, a2, a3]
|
|
|
|
// Go doesn't support this syntax so we create a set of var decls with type assertions
|
|
|
|
// to prepend to the body: a1 := _args[0].(string) ... etc.
|
|
|
|
func (g *generator) rewriteThenForAllApply(
|
|
|
|
then *model.AnonymousFunctionExpression,
|
|
|
|
) (*model.AnonymousFunctionExpression, []string) {
|
2023-06-28 16:02:04 +00:00
|
|
|
typeConvDecls := slice.Prealloc[string](len(then.Parameters))
|
2020-06-19 00:17:51 +00:00
|
|
|
for i, v := range then.Parameters {
|
2024-07-23 07:03:26 +00:00
|
|
|
typ := g.argumentTypeName(v.VariableType, false)
|
2020-06-19 00:17:51 +00:00
|
|
|
decl := fmt.Sprintf("%s := _args[%d].(%s)", v.Name, i, typ)
|
|
|
|
typeConvDecls = append(typeConvDecls, decl)
|
|
|
|
}
|
|
|
|
|
|
|
|
// dummy type that will produce []interface{} for argumentTypeName
|
|
|
|
interfaceArrayType := &model.TupleType{
|
|
|
|
ElementTypes: []model.Type{
|
|
|
|
model.BoolType, model.StringType, model.IntType,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
then.Parameters = []*model.Variable{{
|
|
|
|
Name: "_args",
|
|
|
|
VariableType: interfaceArrayType,
|
|
|
|
}}
|
|
|
|
then.Signature.Parameters = []model.Parameter{{
|
|
|
|
Name: "_args",
|
|
|
|
Type: interfaceArrayType,
|
|
|
|
}}
|
|
|
|
|
|
|
|
return then, typeConvDecls
|
|
|
|
}
|
|
|
|
|
2023-06-22 17:43:06 +00:00
|
|
|
// Writes a Go string literal.
|
|
|
|
// The literal will be a raw string literal if allowRaw is true
|
|
|
|
// and the string is long enough to benefit from it.
|
|
|
|
func (g *generator) genStringLiteral(w io.Writer, v string, allowRaw bool) {
|
|
|
|
// If the string is longer than 50 characters,
|
|
|
|
// contains at least 5 newlines,
|
|
|
|
// and does not contain a backtick,
|
|
|
|
// use a backtick string literal for readability.
|
|
|
|
canBeRaw := len(v) > 50 &&
|
|
|
|
strings.Count(v, "\n") >= 5 &&
|
|
|
|
!strings.Contains(v, "`")
|
|
|
|
if allowRaw && canBeRaw {
|
|
|
|
fmt.Fprintf(w, "`%s`", v)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2020-06-18 20:34:22 +00:00
|
|
|
g.Fgen(w, "\"")
|
|
|
|
g.Fgen(w, g.escapeString(v))
|
|
|
|
g.Fgen(w, "\"")
|
2020-05-27 14:32:13 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (g *generator) escapeString(v string) string {
|
|
|
|
builder := strings.Builder{}
|
|
|
|
for _, c := range v {
|
2024-07-29 16:35:31 +00:00
|
|
|
if c == '\x00' {
|
|
|
|
// escape NUL bytes
|
|
|
|
builder.WriteString(fmt.Sprintf("\\u%04x", c))
|
|
|
|
continue
|
|
|
|
}
|
2020-05-27 14:32:13 +00:00
|
|
|
if c == '"' || c == '\\' {
|
|
|
|
builder.WriteRune('\\')
|
|
|
|
}
|
2020-06-18 20:34:22 +00:00
|
|
|
if c == '\n' {
|
|
|
|
builder.WriteRune('\\')
|
|
|
|
builder.WriteRune('n')
|
|
|
|
continue
|
|
|
|
}
|
2020-05-27 14:32:13 +00:00
|
|
|
builder.WriteRune(c)
|
|
|
|
}
|
|
|
|
return builder.String()
|
|
|
|
}
|
2020-06-06 01:52:00 +00:00
|
|
|
|
2023-01-06 00:07:45 +00:00
|
|
|
//nolint:lll
|
2020-06-06 01:52:00 +00:00
|
|
|
func isInputty(destType model.Type) bool {
|
|
|
|
// TODO this needs to be more robust, likely the inverse of:
|
|
|
|
// https://github.com/pulumi/pulumi/blob/5330c97684cad78bcc60d8867f1b28704bd8a555/pkg/codegen/hcl2/model/type_eventuals.go#L244
|
|
|
|
switch destType := destType.(type) {
|
|
|
|
case *model.UnionType:
|
|
|
|
for _, t := range destType.ElementTypes {
|
|
|
|
if _, ok := t.(*model.OutputType); ok {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
case *model.OutputType:
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
|
|
|
func (g *generator) literalKey(x model.Expression) (string, bool) {
|
Enable some more linting rules (#17456)
Issue #10659 lists a number of extra linting checks that we could enable
in order to make our Go code more robust. This commit implements as many
as seem sensible:
* `durationcheck`, which checks for multiplication of `time.Duration`s,
which can lead to unexpected behaviour (e.g. `time.Second * time.Second`
is *not* one second)
* `goprintffuncname`, which checks that `Printf`-like functions are
appropriately suffixed with `f` to indicate as such
* `tenv`, which checks for `os.Setenv` in tests where `t.Setenv` is
generally a better solution
* `wastedassign`, which checks for assignments whose values are never
used (such as initial values before an `if` where both branches then
overwrite the value)
* `whitespace`, which checks for blank lines at the beginning and end of
blocks such as functions, `if`s, `for`s and so on.
This commit does *not* enable the following checks listed in #10659:
* `wrapcheck`, which insists that third-party library errors are always
`%w`rapped -- we have a lot of cases where we don't do this and it's
probably a bit more involved than "just wrap them" in terms of making
sure we don't break anything (maybe)
* `predeclared`, which checks for shadowing of existing Go identifiers
-- we use `old` and `new` a lot, especially in step generation, so this
is probably a slightly bigger clean-up/one we might want to opt out of
* `mnd` (magic number detection) -- we have a lot of failures on this
* `nilnil` -- we only have a couple of failures on this; these could
probably be handled with `//nolint` but for now I've opted not to take
this route.
2024-10-03 17:37:13 +00:00
|
|
|
var strKey string
|
2020-06-06 01:52:00 +00:00
|
|
|
switch x := x.(type) {
|
|
|
|
case *model.LiteralValueExpression:
|
2021-06-24 16:17:55 +00:00
|
|
|
if model.StringType.AssignableFrom(x.Type()) {
|
2020-06-06 01:52:00 +00:00
|
|
|
strKey = x.Value.AsString()
|
|
|
|
break
|
|
|
|
}
|
|
|
|
var buf bytes.Buffer
|
|
|
|
g.GenLiteralValueExpression(&buf, x)
|
|
|
|
return buf.String(), true
|
|
|
|
case *model.TemplateExpression:
|
|
|
|
if len(x.Parts) == 1 {
|
2021-06-24 16:17:55 +00:00
|
|
|
if lit, ok := x.Parts[0].(*model.LiteralValueExpression); ok && model.StringType.AssignableFrom(lit.Type()) {
|
2020-06-06 01:52:00 +00:00
|
|
|
strKey = lit.Value.AsString()
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
2022-02-08 13:45:24 +00:00
|
|
|
return "", false
|
2020-06-06 01:52:00 +00:00
|
|
|
default:
|
|
|
|
return "", false
|
|
|
|
}
|
|
|
|
|
|
|
|
return strKey, true
|
|
|
|
}
|
|
|
|
|
|
|
|
// functionName computes the go package, module, and name for the given function token.
|
2020-06-18 20:34:22 +00:00
|
|
|
func (g *generator) functionName(tokenArg model.Expression) (string, string, string, hcl.Diagnostics) {
|
2020-06-06 01:52:00 +00:00
|
|
|
token := tokenArg.(*model.TemplateExpression).Parts[0].(*model.LiteralValueExpression).Value.AsString()
|
|
|
|
tokenRange := tokenArg.SyntaxNode().Range()
|
|
|
|
|
|
|
|
// Compute the resource type from the Pulumi type token.
|
2021-09-30 03:11:56 +00:00
|
|
|
pkg, module, member, diagnostics := pcl.DecomposeToken(token, tokenRange)
|
2020-06-06 01:52:00 +00:00
|
|
|
if strings.HasPrefix(member, "get") {
|
2020-06-18 20:34:22 +00:00
|
|
|
if g.useLookupInvokeForm(token) {
|
|
|
|
member = strings.Replace(member, "get", "lookup", 1)
|
|
|
|
}
|
2020-06-06 01:52:00 +00:00
|
|
|
}
|
2022-08-30 00:16:22 +00:00
|
|
|
modOrAlias := g.getModOrAlias(pkg, module, module)
|
2023-02-23 20:51:11 +00:00
|
|
|
mod := strings.ReplaceAll(modOrAlias, "/", ".")
|
2024-09-27 13:25:54 +00:00
|
|
|
return goPackage(pkg), mod, Title(member), diagnostics
|
2020-06-06 01:52:00 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
var functionPackages = map[string][]string{
|
2022-01-21 14:03:25 +00:00
|
|
|
"join": {"strings"},
|
|
|
|
"mimeType": {"mime", "path"},
|
2022-09-10 07:22:11 +00:00
|
|
|
"readDir": {"os"},
|
2023-01-06 22:39:16 +00:00
|
|
|
"readFile": {"os"},
|
|
|
|
"filebase64": {"encoding/base64", "os"},
|
2022-01-21 14:03:25 +00:00
|
|
|
"toBase64": {"encoding/base64"},
|
2022-09-16 23:12:29 +00:00
|
|
|
"fromBase64": {"encoding/base64"},
|
2022-01-21 14:03:25 +00:00
|
|
|
"toJSON": {"encoding/json"},
|
[program-gen] Fix generated utility functions for filebase64, filebase64sha256, sha1 and mimeType (#14857)
# Description
While writing program tests for generated helper utility functions
`filebase64`, `filebase64sha256`, `sha1` and `mimeType` with the idea to
increase code coverage, it turned out that those are completely broken
in all of the languages containing syntax errors, missing imports and
wrong indentation. This PR fixes them and extends the `functions`
program to show how they now look like and to show that they compile.
Also adding example usage of `stack()`, `project()` and `cwd()` in the
test program.
## Checklist
- [ ] I have run `make tidy` to update any new dependencies
- [ ] I have run `make lint` to verify my code passes the lint check
- [ ] I have formatted my code using `gofumpt`
<!--- Please provide details if the checkbox below is to be left
unchecked. -->
- [x] I have added tests that prove my fix is effective or that my
feature works
<!---
User-facing changes require a CHANGELOG entry.
-->
- [x] 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-15 11:26:00 +00:00
|
|
|
"sha1": {"crypto/sha1", "encoding/hex"},
|
|
|
|
"filebase64sha256": {"crypto/sha256", "os"},
|
2022-03-16 00:05:36 +00:00
|
|
|
"cwd": {"os"},
|
2023-06-11 09:05:10 +00:00
|
|
|
"singleOrNone": {"fmt"},
|
2020-06-06 01:52:00 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (g *generator) genFunctionPackages(x *model.FunctionCallExpression) []string {
|
|
|
|
return functionPackages[x.Name]
|
|
|
|
}
|