Skip to content

Commit

Permalink
new rule: deprecated_lookup (#128)
Browse files Browse the repository at this point in the history
Co-authored-by: Ben Drucker <bvdrucker@gmail.com>
  • Loading branch information
Miouge1 and bendrucker authored Sep 13, 2023
1 parent eedaa2b commit 3c3ab13
Show file tree
Hide file tree
Showing 5 changed files with 251 additions and 0 deletions.
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.

## 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())
})
}
}

0 comments on commit 3c3ab13

Please sign in to comment.