@@ -58,63 +58,69 @@ func validateSelfRef(addr addrs.Referenceable, config hcl.Body, providerSchema p
5858 return diags
5959}
6060
61- // validateSelfRefInExpr checks to ensure that a specific expression does not
62- // reference the same block that it is contained within.
63- func validateSelfRefInExpr (addr addrs.Referenceable , expr hcl.Expression ) tfdiags.Diagnostics {
64- var diags tfdiags.Diagnostics
65-
66- addrStrs := make ([]string , 0 , 1 )
67- addrStrs = append (addrStrs , addr .String ())
68- switch tAddr := addr .(type ) {
69- case addrs.ResourceInstance :
70- // A resource instance may not refer to its containing resource either.
71- addrStrs = append (addrStrs , tAddr .ContainingResource ().String ())
72- }
73-
74- refs , _ := langrefs .ReferencesInExpr (addrs .ParseRef , expr )
75- for _ , ref := range refs {
76-
77- for _ , addrStr := range addrStrs {
78- if ref .Subject .String () == addrStr {
79- diags = diags .Append (& hcl.Diagnostic {
80- Severity : hcl .DiagError ,
81- Summary : "Self-referential block" ,
82- Detail : fmt .Sprintf ("Configuration for %s may not refer to itself." , addrStr ),
83- Subject : ref .SourceRange .ToHCL ().Ptr (),
84- })
85- }
61+ // validateMetaSelfRef checks to ensure that a specific meta expression (count /
62+ // for_each) does not reference the resource it is attached to. The behaviour
63+ // is slightly different from validateSelfRef in that this function is only ever
64+ // called from static contexts (ie. before expansion) and as such the address is
65+ // always a Resource.
66+ //
67+ // This also means that often the references will be to instances of the
68+ // resource, so we need to unpack these to the containing resource to compare
69+ // against the static resource. From the perspective of this function
70+ // `test_resource.foo[4]` is considered to be a self reference to
71+ // `test_resource.foo`, in which is a significant behaviour change to
72+ // validateSelfRef.
73+ func validateMetaSelfRef (addr addrs.Resource , expr hcl.Expression ) tfdiags.Diagnostics {
74+ return validateSelfRefFromExprInner (addr , expr , func (ref * addrs.Reference ) * hcl.Diagnostic {
75+ return & hcl.Diagnostic {
76+ Severity : hcl .DiagError ,
77+ Summary : "Self-referential block" ,
78+ Detail : fmt .Sprintf ("Configuration for %s may not refer to itself." , addr .String ()),
79+ Subject : ref .SourceRange .ToHCL ().Ptr (),
8680 }
87- }
88-
89- return diags
81+ })
9082}
9183
92- // validateSelfRefFromImport is similar to validateSelfRefInExpr except it
84+ // validateImportSelfRef is similar to validateMetaSelfRef except it
9385// tweaks the error message slightly to reflect the self-reference is coming
94- // from an import block instead of directly from the resource.
95- func validateSelfRefFromImport (addr addrs.Referenceable , expr hcl.Expression ) tfdiags.Diagnostics {
96- var diags tfdiags.Diagnostics
86+ // from an import block instead of directly from the resource. All the same
87+ // caveats apply as validateMetaSelfRef.
88+ func validateImportSelfRef (addr addrs.Resource , expr hcl.Expression ) tfdiags.Diagnostics {
89+ return validateSelfRefFromExprInner (addr , expr , func (ref * addrs.Reference ) * hcl.Diagnostic {
90+ return & hcl.Diagnostic {
91+ Severity : hcl .DiagError ,
92+ Summary : "Invalid import id argument" ,
93+ Detail : "The import ID cannot reference the resource being imported." ,
94+ Subject : ref .SourceRange .ToHCL ().Ptr (),
95+ }
96+ })
97+ }
9798
98- addrStrs := make ([] string , 0 , 1 )
99- addrStrs = append ( addrStrs , addr . String ())
100- switch tAddr := addr .( type ) {
101- case addrs. ResourceInstance :
102- // A resource instance may not refer to its containing resource either .
103- addrStrs = append ( addrStrs , tAddr . ContainingResource (). String ())
104- }
99+ // validateSelfRefFromExprInner is a helper function that takes an address and
100+ // an expression and returns diagnostics for self-references in the expression.
101+ //
102+ // This should only be called via validateMetaSelfRef and validateImportSelfRef,
103+ // do not access this function directly .
104+ func validateSelfRefFromExprInner ( addr addrs. Resource , expr hcl. Expression , diag func ( ref * addrs. Reference ) * hcl. Diagnostic ) tfdiags. Diagnostics {
105+ var diags tfdiags. Diagnostics
105106
106107 refs , _ := langrefs .ReferencesInExpr (addrs .ParseRef , expr )
107108 for _ , ref := range refs {
109+ var target addrs.Resource
110+ switch t := ref .Subject .(type ) {
111+ case addrs.ResourceInstance :
112+ // Automatically unpack an instance reference to its containing
113+ // resource, since we're only comparing against the static resource.
114+ target = t .Resource
115+ case addrs.Resource :
116+ target = t
117+ default :
118+ // Anything else cannot be a self-reference.
119+ continue
120+ }
108121
109- for _ , addrStr := range addrStrs {
110- if ref .Subject .String () == addrStr {
111- diags = diags .Append (& hcl.Diagnostic {
112- Severity : hcl .DiagError ,
113- Summary : "Invalid import id argument" ,
114- Detail : "The import ID cannot reference the resource being imported." ,
115- Subject : ref .SourceRange .ToHCL ().Ptr (),
116- })
117- }
122+ if target .Equal (addr ) {
123+ diags = diags .Append (diag (ref ))
118124 }
119125 }
120126
0 commit comments