-
Notifications
You must be signed in to change notification settings - Fork 25
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
New Checks: S035 through S037 for invalid attribute references
Reference: #104
- Loading branch information
Showing
35 changed files
with
742 additions
and
50 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
package analysisutils | ||
|
||
import ( | ||
"fmt" | ||
|
||
"github.com/bflad/tfproviderlint/passes/commentignore" | ||
"github.com/bflad/tfproviderlint/passes/helper/schema/schemainfo" | ||
"golang.org/x/tools/go/analysis" | ||
) | ||
|
||
// SchemaAttributeReferencesAnalyzer returns an Analyzer for fields that use schema attribute references | ||
func SchemaAttributeReferencesAnalyzer(analyzerName string, fieldName string) *analysis.Analyzer { | ||
doc := fmt.Sprintf(`check for Schema with invalid %[2]s references | ||
The %[1]s analyzer ensures schema attribute references in the Schema %[2]s | ||
field use valid syntax. The Terraform Plugin SDK can unit test attribute | ||
references to verify the references against the full schema. | ||
`, analyzerName, fieldName) | ||
|
||
return &analysis.Analyzer{ | ||
Name: analyzerName, | ||
Doc: doc, | ||
Requires: []*analysis.Analyzer{ | ||
commentignore.Analyzer, | ||
schemainfo.Analyzer, | ||
}, | ||
Run: SchemaAttributeReferencesRunner(analyzerName, fieldName), | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
package analysisutils | ||
|
||
import ( | ||
"go/ast" | ||
|
||
"github.com/bflad/tfproviderlint/helper/astutils" | ||
"github.com/bflad/tfproviderlint/helper/terraformtype/helper/schema" | ||
"github.com/bflad/tfproviderlint/passes/commentignore" | ||
"github.com/bflad/tfproviderlint/passes/helper/schema/schemainfo" | ||
"golang.org/x/tools/go/analysis" | ||
) | ||
|
||
// SchemaAttributeReferencesRunner returns an Analyzer runner for fields that use schema attribute references | ||
func SchemaAttributeReferencesRunner(analyzerName string, fieldName string) func(*analysis.Pass) (interface{}, error) { | ||
return func(pass *analysis.Pass) (interface{}, error) { | ||
ignorer := pass.ResultOf[commentignore.Analyzer].(*commentignore.Ignorer) | ||
schemaInfos := pass.ResultOf[schemainfo.Analyzer].([]*schema.SchemaInfo) | ||
|
||
for _, schemaInfo := range schemaInfos { | ||
if ignorer.ShouldIgnore(analyzerName, schemaInfo.AstCompositeLit) { | ||
continue | ||
} | ||
|
||
if !schemaInfo.DeclaresField(fieldName) { | ||
continue | ||
} | ||
|
||
switch value := schemaInfo.Fields[fieldName].Value.(type) { | ||
case *ast.CompositeLit: | ||
if !astutils.IsExprTypeArrayString(value.Type) { | ||
continue | ||
} | ||
|
||
for _, elt := range value.Elts { | ||
attributeReference := astutils.ExprStringValue(elt) | ||
|
||
if attributeReference == nil { | ||
continue | ||
} | ||
|
||
if _, err := schema.ParseAttributeReference(*attributeReference); err != nil { | ||
pass.Reportf(elt.Pos(), "%s: invalid %s attribute reference: %s", analyzerName, fieldName, err) | ||
} | ||
} | ||
} | ||
} | ||
|
||
return nil, nil | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
package astutils | ||
|
||
import ( | ||
"go/ast" | ||
) | ||
|
||
// IsExprTypeArrayString returns true if the expression matches []string | ||
func IsExprTypeArrayString(e ast.Expr) bool { | ||
arrayType, ok := e.(*ast.ArrayType) | ||
|
||
return ok && IsExprTypeString(arrayType.Elt) | ||
} | ||
|
||
// IsExprTypeArrayError returns true if the expression matches []error | ||
func IsExprTypeArrayError(e ast.Expr) bool { | ||
arrayType, ok := e.(*ast.ArrayType) | ||
|
||
return ok && IsExprTypeError(arrayType.Elt) | ||
} | ||
|
||
// IsExprTypeError returns true if the expression matches string | ||
func IsExprTypeError(e ast.Expr) bool { | ||
ident, ok := e.(*ast.Ident) | ||
|
||
return ok && ident.Name == "error" | ||
} | ||
|
||
// IsExprTypeInterface returns true if the expression matches interface{} | ||
func IsExprTypeInterface(e ast.Expr) bool { | ||
_, ok := e.(*ast.InterfaceType) | ||
|
||
return ok | ||
} | ||
|
||
// IsExprTypeString returns true if the expression matches string | ||
func IsExprTypeString(e ast.Expr) bool { | ||
ident, ok := e.(*ast.Ident) | ||
|
||
return ok && ident.Name == "string" | ||
} |
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
package schema | ||
|
||
import ( | ||
"fmt" | ||
"math" | ||
"regexp" | ||
"strconv" | ||
"strings" | ||
) | ||
|
||
const ( | ||
// Pattern for schema attribute names | ||
AttributeNameRegexpPattern = `^[a-z0-9_]+$` | ||
|
||
// Pattern for schema references to attributes, such as ConflictsWith values | ||
AttributeReferenceRegexpPattern = `^[a-z0-9_]+(\.[a-z0-9_]+)*$` | ||
) | ||
|
||
var ( | ||
AttributeNameRegexp = regexp.MustCompile(AttributeNameRegexpPattern) | ||
AttributeReferenceRegexp = regexp.MustCompile(AttributeReferenceRegexpPattern) | ||
) | ||
|
||
// ParseAttributeReference validates and returns the split representation of schema attribute reference. | ||
// Attribute references are used in Schema fields such as AtLeastOneOf, ConflictsWith, and ExactlyOneOf. | ||
func ParseAttributeReference(reference string) ([]string, error) { | ||
if !AttributeReferenceRegexp.MatchString(reference) { | ||
return nil, fmt.Errorf("%q must contain only valid attribute names, separated by periods", reference) | ||
} | ||
|
||
attributeReferenceParts := strings.Split(reference, ".") | ||
|
||
if len(attributeReferenceParts) == 1 { | ||
return attributeReferenceParts, nil | ||
} | ||
|
||
configurationBlockReferenceErr := fmt.Errorf("%q configuration block attribute references are only valid for TypeList and MaxItems: 1 attributes and nested attributes must be separated by .0.", reference) | ||
|
||
if math.Mod(float64(len(attributeReferenceParts)), 2) == 0 { | ||
return attributeReferenceParts, configurationBlockReferenceErr | ||
} | ||
|
||
// All even parts of an attribute reference must be 0 | ||
for idx, attributeReferencePart := range attributeReferenceParts { | ||
if math.Mod(float64(idx), 2) == 0 { | ||
continue | ||
} | ||
|
||
attributeReferencePartInt, err := strconv.Atoi(attributeReferencePart) | ||
|
||
if err != nil { | ||
return attributeReferenceParts, configurationBlockReferenceErr | ||
} | ||
|
||
if attributeReferencePartInt != 0 { | ||
return attributeReferenceParts, configurationBlockReferenceErr | ||
} | ||
} | ||
|
||
return attributeReferenceParts, nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
# S035 | ||
|
||
The S035 analyzer reports cases of Schemas which include `AtLeastOneOf` and have invalid schema attribute references. | ||
|
||
NOTE: This only verifies the syntax of attribute references. The Terraform Plugin SDK can unit test attribute references to verify the references against the full schema. | ||
|
||
## Flagged Code | ||
|
||
```go | ||
&schema.Schema{ | ||
AtLeastOneOf: []string{"config_block_attr.nested_attr"}, | ||
} | ||
``` | ||
|
||
## Passing Code | ||
|
||
```go | ||
&schema.Schema{ | ||
AtLeastOneOf: []string{"config_block_attr.0.nested_attr"}, | ||
} | ||
``` | ||
|
||
## Ignoring Reports | ||
|
||
Singular reports can be ignored by adding the a `//lintignore:S035` Go code comment at the end of the offending line or on the line immediately proceding, e.g. | ||
|
||
```go | ||
//lintignore:S035 | ||
&schema.Schema{ | ||
AtLeastOneOf: []string{"config_block_attr.nested_attr"}, | ||
} | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
package S035 | ||
|
||
import ( | ||
"github.com/bflad/tfproviderlint/helper/analysisutils" | ||
"github.com/bflad/tfproviderlint/helper/terraformtype/helper/schema" | ||
) | ||
|
||
var Analyzer = analysisutils.SchemaAttributeReferencesAnalyzer("S035", schema.SchemaFieldAtLeastOneOf) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
package S035 | ||
|
||
import ( | ||
"testing" | ||
|
||
"golang.org/x/tools/go/analysis/analysistest" | ||
) | ||
|
||
func TestS035(t *testing.T) { | ||
testdata := analysistest.TestData() | ||
analysistest.Run(t, testdata, Analyzer, "a") | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
package a | ||
|
||
import ( | ||
s "github.com/hashicorp/terraform-plugin-sdk/helper/schema" | ||
) | ||
|
||
func falias() { | ||
_ = s.Schema{ | ||
AtLeastOneOf: []string{ | ||
".invalidreference", // want "invalid AtLeastOneOf attribute reference" | ||
}, | ||
} | ||
|
||
_ = map[string]*s.Schema{ | ||
"name": { | ||
AtLeastOneOf: []string{ | ||
".invalidreference", // want "invalid AtLeastOneOf attribute reference" | ||
}, | ||
}, | ||
} | ||
} |
Oops, something went wrong.