2020-03-17 03:04:32 +00:00
|
|
|
// 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.
|
|
|
|
|
2021-09-30 03:11:56 +00:00
|
|
|
package pcl
|
2020-03-17 03:04:32 +00:00
|
|
|
|
|
|
|
import (
|
|
|
|
"github.com/hashicorp/hcl/v2"
|
|
|
|
"github.com/hashicorp/hcl/v2/hclsyntax"
|
2021-03-17 13:20:05 +00:00
|
|
|
"github.com/pulumi/pulumi/pkg/v3/codegen"
|
|
|
|
"github.com/pulumi/pulumi/pkg/v3/codegen/hcl2/model"
|
|
|
|
"github.com/pulumi/pulumi/sdk/v3/go/common/util/contract"
|
2020-03-17 03:04:32 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
// bindNode binds a single node in a program. The node's dependencies are bound prior to the node itself; it is an
|
|
|
|
// error for a node to depend--directly or indirectly--upon itself.
|
|
|
|
func (b *binder) bindNode(node Node) hcl.Diagnostics {
|
2020-04-16 23:44:34 +00:00
|
|
|
if node.isBound() {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
if node.isBinding() {
|
|
|
|
// TODO(pdg): print trace
|
|
|
|
rng := node.SyntaxNode().Range()
|
|
|
|
return hcl.Diagnostics{{
|
|
|
|
Severity: hcl.DiagError,
|
|
|
|
Summary: "circular reference",
|
|
|
|
Subject: &rng,
|
|
|
|
}}
|
|
|
|
}
|
|
|
|
node.markBinding()
|
2020-03-17 03:04:32 +00:00
|
|
|
|
2020-04-03 00:38:03 +00:00
|
|
|
var diagnostics hcl.Diagnostics
|
|
|
|
|
2020-03-17 03:04:32 +00:00
|
|
|
deps := b.getDependencies(node)
|
|
|
|
node.setDependencies(deps)
|
|
|
|
|
2020-04-16 23:44:34 +00:00
|
|
|
// Bind any nodes this node depends on.
|
2020-04-03 00:38:03 +00:00
|
|
|
for _, dep := range deps {
|
2020-04-16 23:44:34 +00:00
|
|
|
diags := b.bindNode(dep)
|
|
|
|
diagnostics = append(diagnostics, diags...)
|
2020-04-03 00:38:03 +00:00
|
|
|
}
|
|
|
|
|
2020-03-17 03:04:32 +00:00
|
|
|
switch node := node.(type) {
|
|
|
|
case *ConfigVariable:
|
2020-04-03 00:38:03 +00:00
|
|
|
diags := b.bindConfigVariable(node)
|
|
|
|
diagnostics = append(diagnostics, diags...)
|
2020-03-17 03:04:32 +00:00
|
|
|
case *LocalVariable:
|
2020-04-03 00:38:03 +00:00
|
|
|
diags := b.bindLocalVariable(node)
|
|
|
|
diagnostics = append(diagnostics, diags...)
|
2020-03-17 03:04:32 +00:00
|
|
|
case *Resource:
|
2020-04-03 00:38:03 +00:00
|
|
|
diags := b.bindResource(node)
|
|
|
|
diagnostics = append(diagnostics, diags...)
|
2023-03-08 13:21:34 +00:00
|
|
|
case *Component:
|
|
|
|
diags := b.bindComponent(node)
|
|
|
|
diagnostics = append(diagnostics, diags...)
|
2020-03-17 03:04:32 +00:00
|
|
|
case *OutputVariable:
|
2020-04-03 00:38:03 +00:00
|
|
|
diags := b.bindOutputVariable(node)
|
|
|
|
diagnostics = append(diagnostics, diags...)
|
2020-03-17 03:04:32 +00:00
|
|
|
default:
|
|
|
|
contract.Failf("unexpected node of type %T (%v)", node, node.SyntaxNode().Range())
|
|
|
|
}
|
2020-04-16 23:44:34 +00:00
|
|
|
|
|
|
|
node.markBound()
|
2020-03-17 03:04:32 +00:00
|
|
|
return diagnostics
|
|
|
|
}
|
|
|
|
|
|
|
|
// getDependencies returns the dependencies for the given node.
|
|
|
|
func (b *binder) getDependencies(node Node) []Node {
|
|
|
|
depSet := codegen.Set{}
|
|
|
|
var deps []Node
|
|
|
|
diags := hclsyntax.VisitAll(node.SyntaxNode(), func(node hclsyntax.Node) hcl.Diagnostics {
|
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 depName string
|
2020-03-17 03:04:32 +00:00
|
|
|
switch node := node.(type) {
|
|
|
|
case *hclsyntax.FunctionCallExpr:
|
|
|
|
// TODO(pdg): function scope binds tighter than "normal" scope
|
|
|
|
depName = node.Name
|
|
|
|
case *hclsyntax.ScopeTraversalExpr:
|
|
|
|
depName = node.Traversal.RootName()
|
|
|
|
default:
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// Missing reference errors will be issued during expression binding.
|
|
|
|
referent, _ := b.root.BindReference(depName)
|
|
|
|
if node, ok := referent.(Node); ok && !depSet.Has(node) {
|
|
|
|
depSet.Add(node)
|
|
|
|
deps = append(deps, node)
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
})
|
2023-02-17 01:23:09 +00:00
|
|
|
contract.Assertf(len(diags) == 0, "unexpected diagnostics: %v", diags)
|
2020-03-17 03:04:32 +00:00
|
|
|
return SourceOrderNodes(deps)
|
|
|
|
}
|
|
|
|
|
2023-05-03 22:38:38 +00:00
|
|
|
func expressionIsLiteralNull(expr model.Expression) bool {
|
|
|
|
switch expr := expr.(type) {
|
|
|
|
case *model.LiteralValueExpression:
|
|
|
|
return expr.Value.IsNull()
|
|
|
|
default:
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-03-17 03:04:32 +00:00
|
|
|
func (b *binder) bindConfigVariable(node *ConfigVariable) hcl.Diagnostics {
|
2020-04-30 20:22:24 +00:00
|
|
|
block, diagnostics := model.BindBlock(node.syntax, model.StaticScope(b.root), b.tokens, b.options.modelOptions()...)
|
2020-04-03 00:38:03 +00:00
|
|
|
if defaultValue, ok := block.Body.Attribute("default"); ok {
|
|
|
|
node.DefaultValue = defaultValue.Value
|
2023-05-03 22:38:38 +00:00
|
|
|
// when default is null and the type is not already optional
|
|
|
|
// turn the config type T into option(T)
|
|
|
|
if expressionIsLiteralNull(node.DefaultValue) && !model.IsOptionalType(node.typ) {
|
|
|
|
node.typ = model.NewOptionalType(node.typ)
|
|
|
|
node.Nullable = true
|
|
|
|
}
|
|
|
|
|
2020-04-07 02:43:16 +00:00
|
|
|
if model.InputType(node.typ).ConversionFrom(node.DefaultValue.Type()) == model.NoConversion {
|
2023-05-03 22:38:38 +00:00
|
|
|
errorDiagnostic := model.ExprNotConvertible(model.InputType(node.typ), node.DefaultValue)
|
|
|
|
diagnostics = append(diagnostics, errorDiagnostic)
|
2020-04-03 00:38:03 +00:00
|
|
|
}
|
|
|
|
}
|
2023-05-03 22:38:38 +00:00
|
|
|
|
2022-11-02 19:39:57 +00:00
|
|
|
if attr, ok := block.Body.Attribute(LogicalNamePropertyKey); ok {
|
|
|
|
logicalName, lDiags := getStringAttrValue(attr)
|
|
|
|
if lDiags != nil {
|
|
|
|
diagnostics = diagnostics.Append(lDiags)
|
|
|
|
} else {
|
|
|
|
node.logicalName = logicalName
|
|
|
|
}
|
|
|
|
}
|
2023-03-21 14:01:16 +00:00
|
|
|
|
|
|
|
if descriptionAttr, ok := block.Body.Attribute("description"); ok {
|
|
|
|
description, diags := getStringAttrValue(descriptionAttr)
|
|
|
|
if diags != nil {
|
|
|
|
diagnostics = diagnostics.Append(diags)
|
|
|
|
} else {
|
|
|
|
node.Description = description
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if nullableAttr, ok := block.Body.Attribute("nullable"); ok {
|
|
|
|
nullable, diags := getBooleanAttributeValue(nullableAttr)
|
|
|
|
if diags != nil {
|
|
|
|
diagnostics = diagnostics.Append(diags)
|
|
|
|
} else {
|
|
|
|
node.Nullable = nullable
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-04-21 17:24:42 +00:00
|
|
|
node.Definition = block
|
2020-04-03 00:38:03 +00:00
|
|
|
return diagnostics
|
2020-03-17 03:04:32 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (b *binder) bindLocalVariable(node *LocalVariable) hcl.Diagnostics {
|
2020-04-30 20:22:24 +00:00
|
|
|
attr, diagnostics := model.BindAttribute(node.syntax, b.root, b.tokens, b.options.modelOptions()...)
|
2020-04-21 17:24:42 +00:00
|
|
|
node.Definition = attr
|
2020-04-03 00:38:03 +00:00
|
|
|
return diagnostics
|
2020-03-17 03:04:32 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (b *binder) bindOutputVariable(node *OutputVariable) hcl.Diagnostics {
|
2020-04-30 20:22:24 +00:00
|
|
|
block, diagnostics := model.BindBlock(node.syntax, model.StaticScope(b.root), b.tokens, b.options.modelOptions()...)
|
2022-04-25 22:07:25 +00:00
|
|
|
|
|
|
|
if logicalNameAttr, ok := block.Body.Attribute(LogicalNamePropertyKey); ok {
|
|
|
|
logicalName, lDiags := getStringAttrValue(logicalNameAttr)
|
|
|
|
if lDiags != nil {
|
|
|
|
diagnostics = diagnostics.Append(lDiags)
|
|
|
|
} else {
|
|
|
|
node.logicalName = logicalName
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-04-03 00:38:03 +00:00
|
|
|
if value, ok := block.Body.Attribute("value"); ok {
|
|
|
|
node.Value = value.Value
|
2020-04-07 02:43:16 +00:00
|
|
|
if model.InputType(node.typ).ConversionFrom(node.Value.Type()) == model.NoConversion {
|
|
|
|
diagnostics = append(diagnostics, model.ExprNotConvertible(model.InputType(node.typ), node.Value))
|
2020-04-03 00:38:03 +00:00
|
|
|
}
|
|
|
|
}
|
2020-04-21 17:24:42 +00:00
|
|
|
node.Definition = block
|
2020-04-03 00:38:03 +00:00
|
|
|
return diagnostics
|
2020-03-17 03:04:32 +00:00
|
|
|
}
|