Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

new rule: deprecated_lookup #128

Merged
merged 3 commits into from
Sep 13, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions docs/rules/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ All rules are enabled by default, but by setting `preset = "recommended"`, you c
|[terraform_comment_syntax](terraform_comment_syntax.md)|Disallow `//` comments in favor of `#`||
|[terraform_deprecated_index](terraform_deprecated_index.md)|Disallow legacy dot index syntax|✔|
|[terraform_deprecated_interpolation](terraform_deprecated_interpolation.md)|Disallow deprecated (0.11-style) interpolation|✔|
|[terraform_deprecated_lookup](terraform_deprecated_lookup.md)|Disallow deprecated `lookup()` function with only 2 arguments.||
|[terraform_documented_outputs](terraform_documented_outputs.md)|Disallow `output` declarations without description||
|[terraform_documented_variables](terraform_documented_variables.md)|Disallow `variable` declarations without description||
|[terraform_empty_list_equality](terraform_empty_list_equality.md)|Disallow comparisons with `[]` when checking if a collection is empty|✔|
Expand Down
50 changes: 50 additions & 0 deletions docs/rules/terraform_deprecated_lookup.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
# terraform_deprecated_lookup

Disallow deprecated [`lookup` function](https://developer.hashicorp.com/terraform/language/functions/lookup) usage without a default.

## Example

```hcl
locals {
map = { a = 0 }
value = lookup(local.map, "a")
}
```

```
$ tflint
1 issue(s) found:

Warning: [Fixable] Lookup with 2 arguments is deprecated (terraform_deprecated_lookup)

on main.tf line 3:
3: value = lookup(local.map, "a")

Reference: https://github.com/terraform-linters/tflint-ruleset-terraform/blob/v0.5.0/docs/rules/terraform_deprecated_lookup.md
```

## Why

Calling [`lookup`](https://developer.hashicorp.com/terraform/language/functions/lookup) with 2 arguments has been deprecated since Terraform v0.7. `lookup(map, key)` is equivalent to the native index syntax `map[key]`. `lookup` should only be used with the third `default` argument, even though it is optional for backward compatiblity.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

compatiblity -> compatibility


## How To Fix

Use the native index syntax:

Example:

```hcl
locals {
map = { a = 0 }
value = lookup(local.map, "a")
}
```

Change this to:

```hcl
locals {
map = { a = 0 }
value = local.map["a"]
}
```
1 change: 1 addition & 0 deletions rules/preset.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ var PresetRules = map[string][]tflint.Rule{
NewTerraformUnusedDeclarationsRule(),
NewTerraformUnusedRequiredProvidersRule(),
NewTerraformWorkspaceRemoteRule(),
NewTerraformDeprecatedLookupRule(),
},
"recommended": {
NewTerraformDeprecatedIndexRule(),
Expand Down
81 changes: 81 additions & 0 deletions rules/terraform_deprecated_lookup.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
package rules

import (
"github.com/hashicorp/hcl/v2"
"github.com/hashicorp/hcl/v2/hclsyntax"
"github.com/terraform-linters/tflint-plugin-sdk/tflint"
"github.com/terraform-linters/tflint-ruleset-terraform/project"
)

// TerraformDeprecatedLookupRule warns about usage of the legacy dot syntax for indexes (foo.0)
type TerraformDeprecatedLookupRule struct {
tflint.DefaultRule
}

// NewTerraformDeprecatedIndexRule return a new rule
func NewTerraformDeprecatedLookupRule() *TerraformDeprecatedLookupRule {
return &TerraformDeprecatedLookupRule{}
}

// Name returns the rule name
func (r *TerraformDeprecatedLookupRule) Name() string {
return "terraform_deprecated_lookup"
}

// Enabled returns whether the rule is enabled by default
func (r *TerraformDeprecatedLookupRule) Enabled() bool {
return true
}

// Severity returns the rule severity
func (r *TerraformDeprecatedLookupRule) Severity() tflint.Severity {
return tflint.WARNING
}

// Link returns the rule reference link
func (r *TerraformDeprecatedLookupRule) Link() string {
return project.ReferenceLink(r.Name())
}

// Check walks all expressions and emit issues if deprecated index syntax is found
func (r *TerraformDeprecatedLookupRule) Check(runner tflint.Runner) error {
path, err := runner.GetModulePath()
if err != nil {
return err
}
if !path.IsRoot() {
// This rule does not evaluate child modules.
return nil
}

diags := runner.WalkExpressions(tflint.ExprWalkFunc(func(e hcl.Expression) hcl.Diagnostics {
call, ok := e.(*hclsyntax.FunctionCallExpr)
if ok && call.Name == "lookup" && len(call.Args) == 2 {
if err := runner.EmitIssueWithFix(
r,
"Lookup with 2 arguments is deprecated",
call.Range(),
func(f tflint.Fixer) error {
return f.ReplaceText(call.Range(), f.TextAt(call.Args[0].Range()), "[", f.TextAt(call.Args[1].Range()), "]")
},
); err != nil {
return hcl.Diagnostics{
{
Severity: hcl.DiagError,
Summary: "failed to call EmitIssueWithFix()",
Detail: err.Error(),
},
}
}
return nil
}
return nil
}))
if diags.HasErrors() {
return diags
}

return nil
}


118 changes: 118 additions & 0 deletions rules/terraform_deprecated_lookup_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
package rules

import (
"testing"

"github.com/hashicorp/hcl/v2"
"github.com/terraform-linters/tflint-plugin-sdk/helper"
)

func Test_TerraformDeprecatedLookupRule(t *testing.T) {
cases := []struct {
Name string
Content string
Expected helper.Issues
Fixed string
}{
{
Name: "deprecated lookup",
Content: `
locals {
map = { a = 0 }
value = lookup(local.map, "a")
}
`,
Expected: helper.Issues{
{
Rule: NewTerraformDeprecatedLookupRule(),
Message: "Lookup with 2 arguments is deprecated",
Range: hcl.Range{
Filename: "config.tf",
Start: hcl.Pos{
Line: 4,
Column: 11,
},
End: hcl.Pos{
Line: 4,
Column: 33,
},
},
},
},
Fixed: `
locals {
map = { a = 0 }
value = local.map["a"]
}
`,
},
{
Name: "deprecated lookup nested",
Content: `
locals {
map = { a = { b = 0 } }
value = lookup(lookup(local.map, "a"), "b")
}
`,
Expected: helper.Issues{
{
Rule: NewTerraformDeprecatedLookupRule(),
Message: "Lookup with 2 arguments is deprecated",
Range: hcl.Range{
Filename: "config.tf",
Start: hcl.Pos{
Line: 4,
Column: 11,
},
End: hcl.Pos{
Line: 4,
Column: 46,
},
},
},
{
Rule: NewTerraformDeprecatedLookupRule(),
Message: "Lookup with 2 arguments is deprecated",
Range: hcl.Range{
Filename: "config.tf",
Start: hcl.Pos{
Line: 4,
Column: 18,
},
End: hcl.Pos{
Line: 4,
Column: 40,
},
},
},
},
Fixed: `
locals {
map = { a = { b = 0 } }
value = local.map["a"]["b"]
}
`,
},
}

rule := NewTerraformDeprecatedLookupRule()

for _, tc := range cases {
t.Run(tc.Name, func(t *testing.T) {
filename := "config.tf"

runner := helper.TestRunner(t, map[string]string{filename: tc.Content})

if err := rule.Check(runner); err != nil {
t.Fatalf("Unexpected error occurred: %s", err)
}

helper.AssertIssues(t, tc.Expected, runner.Issues)
want := map[string]string{}
if tc.Fixed != "" {
want[filename] = tc.Fixed
}
helper.AssertChanges(t, want, runner.Changes())
})
}
}