2023-04-10 15:36:59 +00:00
|
|
|
// Copyright (c) HashiCorp, Inc.
|
|
|
|
// SPDX-License-Identifier: MPL-2.0
|
|
|
|
|
2020-11-09 19:35:02 +00:00
|
|
|
package addrs
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
|
|
|
|
|
|
|
"github.com/hashicorp/hcl/v2"
|
|
|
|
)
|
|
|
|
|
|
|
|
// Reference describes a reference to an address with source location
|
|
|
|
// information.
|
|
|
|
type Reference struct {
|
|
|
|
Subject Referenceable
|
|
|
|
SourceRange hcl.Range
|
|
|
|
Remaining hcl.Traversal
|
|
|
|
}
|
|
|
|
|
|
|
|
// ParseRef attempts to extract a referencable address from the prefix of the
|
|
|
|
// given traversal, which must be an absolute traversal or this function
|
|
|
|
// will panic.
|
|
|
|
//
|
|
|
|
// If no error diagnostics are returned, the returned reference includes the
|
|
|
|
// address that was extracted, the source range it was extracted from, and any
|
|
|
|
// remaining relative traversal that was not consumed as part of the
|
|
|
|
// reference.
|
|
|
|
//
|
|
|
|
// If error diagnostics are returned then the Reference value is invalid and
|
|
|
|
// must not be used.
|
|
|
|
func ParseRef(traversal hcl.Traversal) (*Reference, hcl.Diagnostics) {
|
|
|
|
ref, diags := parseRef(traversal)
|
|
|
|
|
|
|
|
// Normalize a little to make life easier for callers.
|
|
|
|
if ref != nil {
|
|
|
|
if len(ref.Remaining) == 0 {
|
|
|
|
ref.Remaining = nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return ref, diags
|
|
|
|
}
|
|
|
|
|
|
|
|
func parseRef(traversal hcl.Traversal) (*Reference, hcl.Diagnostics) {
|
|
|
|
var diags hcl.Diagnostics
|
|
|
|
|
|
|
|
root := traversal.RootName()
|
|
|
|
rootRange := traversal[0].SourceRange()
|
|
|
|
|
|
|
|
switch root {
|
|
|
|
|
|
|
|
case "var":
|
|
|
|
name, rng, remain, diags := parseSingleAttrRef(traversal)
|
|
|
|
return &Reference{
|
|
|
|
Subject: InputVariable{Name: name},
|
|
|
|
SourceRange: rng,
|
|
|
|
Remaining: remain,
|
|
|
|
}, diags
|
|
|
|
|
|
|
|
default:
|
|
|
|
diags = append(diags, &hcl.Diagnostic{
|
|
|
|
Severity: hcl.DiagError,
|
|
|
|
Summary: "Unhandled reference type",
|
|
|
|
Detail: `Currently parseRef can only parse "var" references.`,
|
|
|
|
Subject: &rootRange,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
return nil, diags
|
|
|
|
}
|
|
|
|
|
|
|
|
func parseSingleAttrRef(traversal hcl.Traversal) (string, hcl.Range, hcl.Traversal, hcl.Diagnostics) {
|
|
|
|
var diags hcl.Diagnostics
|
|
|
|
|
|
|
|
root := traversal.RootName()
|
|
|
|
rootRange := traversal[0].SourceRange()
|
|
|
|
|
|
|
|
if len(traversal) < 2 {
|
|
|
|
diags = append(diags, &hcl.Diagnostic{
|
|
|
|
Severity: hcl.DiagError,
|
|
|
|
Summary: "Invalid reference",
|
|
|
|
Detail: fmt.Sprintf("The %q object cannot be accessed directly. Instead, access one of its attributes.", root),
|
|
|
|
Subject: &rootRange,
|
|
|
|
})
|
|
|
|
return "", hcl.Range{}, nil, diags
|
|
|
|
}
|
|
|
|
if attrTrav, ok := traversal[1].(hcl.TraverseAttr); ok {
|
|
|
|
return attrTrav.Name, hcl.RangeBetween(rootRange, attrTrav.SrcRange), traversal[2:], diags
|
|
|
|
}
|
|
|
|
diags = diags.Append(&hcl.Diagnostic{
|
|
|
|
Severity: hcl.DiagError,
|
|
|
|
Summary: "Invalid reference",
|
|
|
|
Detail: fmt.Sprintf("The %q object does not support this operation.", root),
|
|
|
|
Subject: traversal[1].SourceRange().Ptr(),
|
|
|
|
})
|
|
|
|
return "", hcl.Range{}, nil, diags
|
|
|
|
}
|