Skip to content

Commit

Permalink
Add tflint-ignore-file annotation (#1909)
Browse files Browse the repository at this point in the history
  • Loading branch information
wata727 committed Nov 12, 2023
1 parent 31943db commit 8902275
Show file tree
Hide file tree
Showing 4 changed files with 384 additions and 89 deletions.
25 changes: 25 additions & 0 deletions docs/user-guide/annotations.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,3 +54,28 @@ resource "aws_instance" "foo" {
instance_type = "t10.2xlarge"
}
```

To disable an entire file, you can also use the `tflint-ignore-file` annotation:

```hcl
# tflint-ignore-file: aws_instance_invalid_type
resource "aws_instance" "foo" {
instance_type = "t1.2xlarge"
}
```

This annotation is valid only at the top of the file. The following cannot be used and will result in an error:

```hcl
resource "aws_instance" "foo" {
# tflint-ignore-file: aws_instance_invalid_type
instance_type = "t1.2xlarge"
}
```

```hcl
resource "aws_instance" "foo" { # tflint-ignore-file: aws_instance_invalid_type
instance_type = "t1.2xlarge"
}
```
86 changes: 71 additions & 15 deletions tflint/annotation.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,13 @@ import (
"github.com/hashicorp/hcl/v2/hclsyntax"
)

var annotationPattern = regexp.MustCompile(`tflint-ignore: ([^\n*/#]+)`)

// Annotation represents comments with special meaning in TFLint
type Annotation struct {
Content string
Token hclsyntax.Token
type Annotation interface {
IsAffected(*Issue) bool
String() string
}

// Annotations is slice of Annotation
// Annotations is a slice of Annotation
type Annotations []Annotation

// NewAnnotations find annotations from the passed tokens and return that list.
Expand All @@ -35,21 +33,49 @@ func NewAnnotations(path string, file *hcl.File) (Annotations, hcl.Diagnostics)
continue
}

match := annotationPattern.FindStringSubmatch(string(token.Bytes))
if len(match) != 2 {
// tflint-ignore annotation
match := lineAnnotationPattern.FindStringSubmatch(string(token.Bytes))
if len(match) == 2 {
ret = append(ret, &LineAnnotation{
Content: strings.TrimSpace(match[1]),
Token: token,
})
continue
}

// tflint-ignore-file annotation
match = fileAnnotationPattern.FindStringSubmatch(string(token.Bytes))
if len(match) == 2 {
if !(token.Range.Start.Line == 1 && token.Range.Start.Column == 1) {
diags = append(diags, &hcl.Diagnostic{
Severity: hcl.DiagError,
Summary: "tflint-ignore-file annotation must be written at the top of file",
Detail: fmt.Sprintf("tflint-ignore-file annotation is written at line %d, column %d", token.Range.Start.Line, token.Range.Start.Column),
Subject: token.Range.Ptr(),
})
continue
}
ret = append(ret, &FileAnnotation{
Content: strings.TrimSpace(match[1]),
Token: token,
})
continue
}
ret = append(ret, Annotation{
Content: strings.TrimSpace(match[1]),
Token: token,
})
}

return ret, diags
}

var lineAnnotationPattern = regexp.MustCompile(`tflint-ignore: ([^\n*/#]+)`)

// LineAnnotation is an annotation for ignoring issues in a line
type LineAnnotation struct {
Content string
Token hclsyntax.Token
}

// IsAffected checks if the passed issue is affected with the annotation
func (a *Annotation) IsAffected(issue *Issue) bool {
func (a *LineAnnotation) IsAffected(issue *Issue) bool {
if a.Token.Range.Filename != issue.Range.Filename {
return false
}
Expand All @@ -71,6 +97,36 @@ func (a *Annotation) IsAffected(issue *Issue) bool {
}

// String returns the string representation of the annotation
func (a *Annotation) String() string {
return fmt.Sprintf("annotation:%s (%s)", a.Content, a.Token.Range.String())
func (a *LineAnnotation) String() string {
return fmt.Sprintf("tflint-ignore: %s (%s)", a.Content, a.Token.Range.String())
}

var fileAnnotationPattern = regexp.MustCompile(`tflint-ignore-file: ([^\n*/#]+)`)

// FileAnnotation is an annotation for ignoring issues in a file
type FileAnnotation struct {
Content string
Token hclsyntax.Token
}

// IsAffected checks if the passed issue is affected with the annotation
func (a *FileAnnotation) IsAffected(issue *Issue) bool {
if a.Token.Range.Filename != issue.Range.Filename {
return false
}

rules := strings.Split(a.Content, ",")
for i, rule := range rules {
rules[i] = strings.TrimSpace(rule)
}

if slices.Contains(rules, issue.Rule.Name()) || slices.Contains(rules, "all") {
return true
}
return false
}

// String returns the string representation of the annotation
func (a *FileAnnotation) String() string {
return fmt.Sprintf("tflint-ignore-file: %s (%s)", a.Content, a.Token.Range.String())
}
Loading

0 comments on commit 8902275

Please sign in to comment.