github.com/jpreese/tflint@v0.19.2-0.20200908152133-b01686250fb6/rules/terraformrules/terraform_unused_declaration.go (about) 1 package terraformrules 2 3 import ( 4 "fmt" 5 "log" 6 7 "github.com/hashicorp/hcl/v2" 8 "github.com/hashicorp/terraform/addrs" 9 "github.com/hashicorp/terraform/configs" 10 "github.com/hashicorp/terraform/lang" 11 "github.com/terraform-linters/tflint/tflint" 12 ) 13 14 // TerraformUnusedDeclarationsRule checks whether variables, data sources, or locals are declared but unused 15 type TerraformUnusedDeclarationsRule struct{} 16 17 type declarations struct { 18 Variables map[string]*configs.Variable 19 DataResources map[string]*configs.Resource 20 Locals map[string]*configs.Local 21 } 22 23 // NewTerraformUnusedDeclarationsRule returns a new rule 24 func NewTerraformUnusedDeclarationsRule() *TerraformUnusedDeclarationsRule { 25 return &TerraformUnusedDeclarationsRule{} 26 } 27 28 // Name returns the rule name 29 func (r *TerraformUnusedDeclarationsRule) Name() string { 30 return "terraform_unused_declarations" 31 } 32 33 // Enabled returns whether the rule is enabled by default 34 func (r *TerraformUnusedDeclarationsRule) Enabled() bool { 35 return false 36 } 37 38 // Severity returns the rule severity 39 func (r *TerraformUnusedDeclarationsRule) Severity() string { 40 return tflint.WARNING 41 } 42 43 // Link returns the rule reference link 44 func (r *TerraformUnusedDeclarationsRule) Link() string { 45 return tflint.ReferenceLink(r.Name()) 46 } 47 48 // Check emits issues for any variables, locals, and data sources that are declared but not used 49 func (r *TerraformUnusedDeclarationsRule) Check(runner *tflint.Runner) error { 50 if !runner.TFConfig.Path.IsRoot() { 51 // This rule does not evaluate child modules. 52 return nil 53 } 54 55 log.Printf("[TRACE] Check `%s` rule for `%s` runner", r.Name(), runner.TFConfigPath()) 56 57 decl := r.declarations(runner.TFConfig.Module) 58 err := runner.WalkExpressions(func(expr hcl.Expression) error { 59 return r.checkForRefsInExpr(expr, decl) 60 }) 61 if err != nil { 62 return err 63 } 64 65 for _, variable := range decl.Variables { 66 runner.EmitIssue( 67 r, 68 fmt.Sprintf(`variable "%s" is declared but not used`, variable.Name), 69 variable.DeclRange, 70 ) 71 } 72 for _, data := range decl.DataResources { 73 runner.EmitIssue( 74 r, 75 fmt.Sprintf(`data "%s" "%s" is declared but not used`, data.Type, data.Name), 76 data.DeclRange, 77 ) 78 } 79 for _, local := range decl.Locals { 80 runner.EmitIssue( 81 r, 82 fmt.Sprintf(`local.%s is declared but not used`, local.Name), 83 local.DeclRange, 84 ) 85 } 86 87 return nil 88 } 89 90 func (r *TerraformUnusedDeclarationsRule) declarations(module *configs.Module) *declarations { 91 decl := &declarations{ 92 Variables: make(map[string]*configs.Variable, len(module.Variables)), 93 DataResources: make(map[string]*configs.Resource, len(module.DataResources)), 94 Locals: make(map[string]*configs.Local, len(module.Locals)), 95 } 96 97 for k, v := range module.Variables { 98 decl.Variables[k] = v 99 } 100 for k, v := range module.DataResources { 101 decl.DataResources[k] = v 102 } 103 for k, v := range module.Locals { 104 decl.Locals[k] = v 105 } 106 107 return decl 108 } 109 110 func (r *TerraformUnusedDeclarationsRule) checkForRefsInExpr(expr hcl.Expression, decl *declarations) error { 111 refs, diags := lang.ReferencesInExpr(expr) 112 if diags.HasErrors() { 113 log.Printf("[DEBUG] Cannot find references in expression, ignoring: %v", diags.Err()) 114 return nil 115 } 116 117 for _, ref := range refs { 118 switch sub := ref.Subject.(type) { 119 case addrs.InputVariable: 120 delete(decl.Variables, sub.Name) 121 case addrs.LocalValue: 122 delete(decl.Locals, sub.Name) 123 case addrs.Resource: 124 delete(decl.DataResources, sub.String()) 125 case addrs.ResourceInstance: 126 delete(decl.DataResources, sub.Resource.String()) 127 } 128 } 129 130 return nil 131 }