Skip to content

Commit b26ad69

Browse files
Nurullokhon Gulomkodirovintellij-monorepo-bot
authored andcommitted
[terraform] IJPL-185785 Refactored 'self' identifier, completed only computed properties(attributes), added tests
GitOrigin-RevId: 66c022804e27ce4b89e4b3d923ee096ff42dc481
1 parent 63afbd3 commit b26ad69

File tree

11 files changed

+124
-46
lines changed

11 files changed

+124
-46
lines changed

terraform/src/org/intellij/terraform/config/Constants.kt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
1+
// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
22
package org.intellij.terraform.config
33

44
import com.intellij.openapi.util.NlsSafe
@@ -34,6 +34,7 @@ object Constants {
3434
internal const val HCL_PRECONDITION_BLOCK_IDENTIFIER: String = "precondition"
3535
internal const val HCL_POSTCONDITION_BLOCK_IDENTIFIER: String = "postcondition"
3636
internal const val HCL_EPHEMERAL_IDENTIFIER: String = "ephemeral"
37+
internal const val HCL_SELF_IDENTIFIER: String = "self"
3738

3839
internal const val REGISTRY_DOMAIN: String = "registry.terraform.io"
3940
internal const val LATEST_VERSION: String = "latest"

terraform/src/org/intellij/terraform/config/codeinsight/TfCompletionUtil.kt

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import com.intellij.psi.codeStyle.CodeStyleManager
1919
import com.intellij.psi.impl.DebugUtil
2020
import org.intellij.terraform.TerraformIcons
2121
import org.intellij.terraform.config.Constants.HCL_EPHEMERAL_IDENTIFIER
22+
import org.intellij.terraform.config.Constants.HCL_SELF_IDENTIFIER
2223
import org.intellij.terraform.config.TerraformFileType
2324
import org.intellij.terraform.config.documentation.psi.HCLFakeElementPsiFactory
2425
import org.intellij.terraform.config.model.*
@@ -37,7 +38,7 @@ import java.util.*
3738
import javax.swing.Icon
3839

3940
internal object TfCompletionUtil {
40-
val Scopes: Set<String> = setOf("data", "var", "self", "path", "count", "terraform", "local", "module", HCL_EPHEMERAL_IDENTIFIER) + OpenTofuScopes
41+
val Scopes: Set<String> = setOf("data", "var", HCL_SELF_IDENTIFIER, "path", "count", "terraform", "local", "module", HCL_EPHEMERAL_IDENTIFIER) + OpenTofuScopes
4142
val GlobalScopes: SortedSet<String> = (setOf("var", "path", "data", "module", "local", HCL_EPHEMERAL_IDENTIFIER) + OpenTofuScopes).toSortedSet()
4243
val RootBlockKeywords: Set<String> = TypeModel.RootBlocksMap.keys
4344
val RootBlockSorted: List<BlockType> = TypeModel.RootBlocks.sortedBy { it.literal }
@@ -58,7 +59,7 @@ internal object TfCompletionUtil {
5859
)
5960
}
6061

61-
fun createScope(value: String): LookupElementBuilder = LookupElementBuilder.create(value)
62+
fun createScopeLookup(value: String): LookupElementBuilder = LookupElementBuilder.create(value)
6263
.withInsertHandler(ScopeSelectInsertHandler)
6364
.withRenderer(object : LookupElementRenderer<LookupElement?>() {
6465
override fun renderElement(element: LookupElement?, presentation: LookupElementPresentation?) {

terraform/src/org/intellij/terraform/hil/codeinsight/HILCompletionContributor.kt

Lines changed: 18 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,10 @@ import com.intellij.psi.util.PsiTreeUtil
1616
import com.intellij.util.ProcessingContext
1717
import org.intellij.terraform.config.Constants
1818
import org.intellij.terraform.config.Constants.HCL_EPHEMERAL_IDENTIFIER
19+
import org.intellij.terraform.config.Constants.HCL_SELF_IDENTIFIER
1920
import org.intellij.terraform.config.codeinsight.TfCompletionUtil.GlobalScopes
2021
import org.intellij.terraform.config.codeinsight.TfCompletionUtil.createFunction
21-
import org.intellij.terraform.config.codeinsight.TfCompletionUtil.createScope
22+
import org.intellij.terraform.config.codeinsight.TfCompletionUtil.createScopeLookup
2223
import org.intellij.terraform.config.codeinsight.TfConfigCompletionContributor
2324
import org.intellij.terraform.config.codeinsight.TfLookupElementRenderer
2425
import org.intellij.terraform.config.codeinsight.TfModelHelper
@@ -123,21 +124,19 @@ open class HILCompletionContributor : CompletionContributor(), DumbAware {
123124
}
124125
}
125126

126-
private object SelfCompletionProvider : SelectFromScopeCompletionProvider("self") {
127-
override fun doAddCompletions(variable: Identifier,
128-
parameters: CompletionParameters,
129-
context: ProcessingContext,
130-
result: CompletionResultSet) {
131-
// For now 'self' allowed only for provisioners inside resources
132-
127+
private object SelfCompletionProvider : SelectFromScopeCompletionProvider(HCL_SELF_IDENTIFIER) {
128+
override fun doAddCompletions(
129+
variable: Identifier,
130+
parameters: CompletionParameters,
131+
context: ProcessingContext,
132+
result: CompletionResultSet,
133+
) {
133134
val resource = getHclBlockForSelfContext(variable) ?: return
134-
val properties = TfModelHelper.getBlockProperties(resource)
135-
// TODO: Filter already defined or computed properties (?)
136-
// TODO: Add type filtration
137-
val set = properties.keys.toHashSet()
138-
set.remove(Constants.HAS_DYNAMIC_ATTRIBUTES)
139-
resource.`object`?.propertyList?.mapTo(set) { it.name }
140-
result.addAllElements(set.map { create(it) })
135+
val properties = TfModelHelper.getBlockProperties(resource).filter {
136+
it.key != Constants.HAS_DYNAMIC_ATTRIBUTES &&
137+
it.value.computed
138+
}
139+
result.addAllElements(properties.keys.map { create(it) })
141140
}
142141
}
143142

@@ -272,17 +271,17 @@ open class HILCompletionContributor : CompletionContributor(), DumbAware {
272271
result.addAllElements(model.functions.map { createFunction(it) })
273272
result.addAllElements(model.providerDefinedFunctions.map { createFunction(it) })
274273

275-
result.addAllElements(GlobalScopes.map { createScope(it) })
276-
if (getHclBlockForSelfContext(parent) != null) result.addElement(createScope("self"))
274+
result.addAllElements(GlobalScopes.map { createScopeLookup(it) })
275+
if (getHclBlockForSelfContext(parent) != null) result.addElement(createScopeLookup(HCL_SELF_IDENTIFIER))
277276

278277
val host = parent.getHCLHost() ?: return
279278
val resourceOrDataSource = getContainingResourceOrDataSourceOrModule(host)
280279
if (resourceOrDataSource != null) {
281280
if (resourceOrDataSource.`object`?.findProperty("for_each") != null) {
282-
result.addElement(createScope("each"))
281+
result.addElement(createScopeLookup("each"))
283282
}
284283
if (resourceOrDataSource.`object`?.findProperty("count") != null) {
285-
result.addElement(createScope("count"))
284+
result.addElement(createScopeLookup("count"))
286285
}
287286
}
288287
}

terraform/src/org/intellij/terraform/hil/inspection/HILMissingSelfInContextInspection.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import com.intellij.lang.injection.InjectedLanguageManager
88
import com.intellij.openapi.progress.ProgressIndicatorProvider
99
import com.intellij.psi.PsiElementVisitor
1010
import com.intellij.psi.PsiFile
11+
import org.intellij.terraform.config.Constants.HCL_SELF_IDENTIFIER
1112
import org.intellij.terraform.hcl.HCLBundle
1213
import org.intellij.terraform.hcl.psi.getConnectionOfResource
1314
import org.intellij.terraform.hcl.psi.getProvisionerOfResource
@@ -31,8 +32,7 @@ class HILMissingSelfInContextInspection : LocalInspectionTool() {
3132
inner class MyEV(val holder: ProblemsHolder) : ILElementVisitor() {
3233
override fun visitILVariable(element: ILVariable) {
3334
ProgressIndicatorProvider.checkCanceled()
34-
val name = element.name
35-
if ("self" != name) return
35+
if (element.name != HCL_SELF_IDENTIFIER) return
3636

3737
val host = element.getHCLHost() ?: return
3838
val parent = element.parent as? ILSelectExpression ?: return

terraform/src/org/intellij/terraform/hil/psi/ILScopeReferenceProvider.kt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import com.intellij.psi.PsiElement
55
import com.intellij.psi.PsiReference
66
import com.intellij.psi.PsiReferenceProvider
77
import com.intellij.util.ProcessingContext
8+
import org.intellij.terraform.config.Constants.HCL_SELF_IDENTIFIER
89
import org.intellij.terraform.hcl.psi.common.Identifier
910
import org.intellij.terraform.hcl.psi.common.SelectExpression
1011
import org.intellij.terraform.hcl.psi.getDataSource
@@ -27,7 +28,7 @@ object ILScopeReferenceProvider : PsiReferenceProvider() {
2728
if (from !== element) return PsiReference.EMPTY_ARRAY
2829

2930
when (element.name) {
30-
"self" -> {
31+
HCL_SELF_IDENTIFIER -> {
3132
return arrayOf(HCLElementLazyReference(element, false) { _, _ ->
3233
val resource = getHclBlockForSelfContext(this.element) ?: return@HCLElementLazyReference emptyList()
3334
listOf(resource)

terraform/src/org/intellij/terraform/hil/psi/ILSelectFromScopeReferenceProvider.kt

Lines changed: 9 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import com.intellij.psi.PsiReference
88
import com.intellij.psi.PsiReferenceBase
99
import com.intellij.psi.PsiReferenceProvider
1010
import com.intellij.util.ProcessingContext
11+
import org.intellij.terraform.config.Constants.HCL_SELF_IDENTIFIER
1112
import org.intellij.terraform.config.codeinsight.TfModelHelper
1213
import org.intellij.terraform.config.model.getTerraformModule
1314
import org.intellij.terraform.hcl.psi.HCLElement
@@ -30,29 +31,21 @@ object ILSelectFromScopeReferenceProvider : PsiReferenceProvider() {
3031
val from = parent.from as? Identifier ?: return PsiReference.EMPTY_ARRAY
3132

3233
if (from === element) return PsiReference.EMPTY_ARRAY
33-
34-
when (from.name) {
35-
"var" -> {
36-
return arrayOf(VariableReference(element))
37-
}
38-
"self" -> {
39-
return arrayOf(SelfResourceReference(element))
40-
}
34+
return when (from.name) {
35+
"var" -> arrayOf(VariableReference(element))
36+
HCL_SELF_IDENTIFIER -> arrayOf(SelfResourceReference(element))
4137
"path" -> {
4238
// TODO: Resolve 'cwd' and 'root' paths
4339
if (element.name == "module") {
4440
val file = host.containingFile.originalFile
4541
return arrayOf(PsiReferenceBase.Immediate(element, true, file.containingDirectory ?: file))
4642
}
43+
else PsiReference.EMPTY_ARRAY
4744
}
48-
"module" -> {
49-
return arrayOf(ModuleReference(element))
50-
}
51-
"local" -> {
52-
return arrayOf(LocalVariableReference(element))
53-
}
45+
"module" -> arrayOf(ModuleReference(element))
46+
"local" -> arrayOf(LocalVariableReference(element))
47+
else -> return PsiReference.EMPTY_ARRAY
5448
}
55-
return PsiReference.EMPTY_ARRAY
5649
}
5750

5851
class VariableReference(element: Identifier) : HCLElementLazyReference<Identifier>(element, false, doResolve = { _, _ ->
@@ -88,7 +81,7 @@ object ILSelectFromScopeReferenceProvider : PsiReferenceProvider() {
8881

8982
class ModuleReference(element: Identifier) : HCLElementLazyReference<Identifier>(element, false, doResolve = { _, _ ->
9083
this.element.name?.let { name -> this.element.getHCLHost()?.getTerraformModule()?.getDefinedModules(name) }
91-
?: emptyList()
84+
?: emptyList()
9285
})
9386

9487
class LocalVariableReference(element: Identifier) : HCLElementLazyReference<Identifier>(element, false, doResolve = { _, _ ->
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
<?xml version="1.0" encoding="UTF-8" ?>
2+
<problems>
3+
</problems>
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
data "aws_ami" "example" {
2+
owners = ["amazon"]
3+
4+
filter {
5+
name = "image-id"
6+
values = ["ami-abc123"]
7+
}
8+
}
9+
10+
resource "aws_instance" "example" {
11+
instance_type = "t3.micro"
12+
ami = data.aws_ami.example.id
13+
14+
lifecycle {
15+
16+
# The EC2 instance must be allocated a public DNS hostname.
17+
postcondition {
18+
condition = self.public_dns != ""
19+
error_message = "EC2 instance must be in a VPC that has public DNS hostnames enabled."
20+
}
21+
}
22+
}
23+
24+
data "aws_ebs_volume" "example" {
25+
# Whenever a data resource is verifying the result of a managed resource
26+
# declared in the same configuration, you MUST write the checks as
27+
# postconditions of the data resource. This ensures Terraform will wait
28+
# to read the data resource until after any changes to the managed resource
29+
# have completed.
30+
lifecycle {
31+
# The EC2 instance will have an encrypted root volume.
32+
postcondition {
33+
condition = self.encrypted
34+
error_message = "The server's root volume is not encrypted."
35+
}
36+
}
37+
}
38+
39+
output "api_base_url" {
40+
value = "https://${aws_instance.example.private_dns}:8433/"
41+
}

terraform/test/org/intellij/terraform/config/TfInspectionsTest.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,10 @@ public void testSelfReferences() {
6363
doTest("self_references", new HILUnresolvedReferenceInspection());
6464
}
6565

66+
public void testSelfReferenceInPostCondition() {
67+
doTest("self_reference_in_post_condition", new HILUnresolvedReferenceInspection());
68+
}
69+
6670
public void testComplexPropertyKeys() {
6771
doTest("complex_property_keys", new HILUnresolvedReferenceInspection());
6872
}

terraform/test/org/intellij/terraform/config/codeinsight/TfConfigCompletionTest.kt

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1029,6 +1029,24 @@ internal class TfConfigCompletionTest : TfBaseCompletionTestCase() {
10291029
}
10301030
""".trimIndent(), 0)
10311031
}
1032+
1033+
fun testSelfReferenceInPostCondition() {
1034+
doBasicCompletionTest(
1035+
"""
1036+
resource "aws_instance" "example" {
1037+
instance_type = "t2.micro"
1038+
ami = "ami-abc123"
1039+
1040+
lifecycle {
1041+
postcondition {
1042+
condition = self.<caret> == "running"
1043+
error_message = "EC2 instance must be running."
1044+
}
1045+
}
1046+
}
1047+
""".trimIndent(),
1048+
"arn", "id", "instance_state", "user_data", "password_data")
1049+
}
10321050
}
10331051

10341052
private const val ENTRIES_LIST_SIZE = 900

0 commit comments

Comments
 (0)