Skip to content

Commit 8902275

Browse files
authored
Add tflint-ignore-file annotation (terraform-linters#1909)
1 parent 31943db commit 8902275

File tree

4 files changed

+384
-89
lines changed

4 files changed

+384
-89
lines changed

docs/user-guide/annotations.md

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,3 +54,28 @@ resource "aws_instance" "foo" {
5454
instance_type = "t10.2xlarge"
5555
}
5656
```
57+
58+
To disable an entire file, you can also use the `tflint-ignore-file` annotation:
59+
60+
```hcl
61+
# tflint-ignore-file: aws_instance_invalid_type
62+
63+
resource "aws_instance" "foo" {
64+
instance_type = "t1.2xlarge"
65+
}
66+
```
67+
68+
This annotation is valid only at the top of the file. The following cannot be used and will result in an error:
69+
70+
```hcl
71+
resource "aws_instance" "foo" {
72+
# tflint-ignore-file: aws_instance_invalid_type
73+
instance_type = "t1.2xlarge"
74+
}
75+
```
76+
77+
```hcl
78+
resource "aws_instance" "foo" { # tflint-ignore-file: aws_instance_invalid_type
79+
instance_type = "t1.2xlarge"
80+
}
81+
```

tflint/annotation.go

Lines changed: 71 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -10,15 +10,13 @@ import (
1010
"github.com/hashicorp/hcl/v2/hclsyntax"
1111
)
1212

13-
var annotationPattern = regexp.MustCompile(`tflint-ignore: ([^\n*/#]+)`)
14-
1513
// Annotation represents comments with special meaning in TFLint
16-
type Annotation struct {
17-
Content string
18-
Token hclsyntax.Token
14+
type Annotation interface {
15+
IsAffected(*Issue) bool
16+
String() string
1917
}
2018

21-
// Annotations is slice of Annotation
19+
// Annotations is a slice of Annotation
2220
type Annotations []Annotation
2321

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

38-
match := annotationPattern.FindStringSubmatch(string(token.Bytes))
39-
if len(match) != 2 {
36+
// tflint-ignore annotation
37+
match := lineAnnotationPattern.FindStringSubmatch(string(token.Bytes))
38+
if len(match) == 2 {
39+
ret = append(ret, &LineAnnotation{
40+
Content: strings.TrimSpace(match[1]),
41+
Token: token,
42+
})
43+
continue
44+
}
45+
46+
// tflint-ignore-file annotation
47+
match = fileAnnotationPattern.FindStringSubmatch(string(token.Bytes))
48+
if len(match) == 2 {
49+
if !(token.Range.Start.Line == 1 && token.Range.Start.Column == 1) {
50+
diags = append(diags, &hcl.Diagnostic{
51+
Severity: hcl.DiagError,
52+
Summary: "tflint-ignore-file annotation must be written at the top of file",
53+
Detail: fmt.Sprintf("tflint-ignore-file annotation is written at line %d, column %d", token.Range.Start.Line, token.Range.Start.Column),
54+
Subject: token.Range.Ptr(),
55+
})
56+
continue
57+
}
58+
ret = append(ret, &FileAnnotation{
59+
Content: strings.TrimSpace(match[1]),
60+
Token: token,
61+
})
4062
continue
4163
}
42-
ret = append(ret, Annotation{
43-
Content: strings.TrimSpace(match[1]),
44-
Token: token,
45-
})
4664
}
4765

4866
return ret, diags
4967
}
5068

69+
var lineAnnotationPattern = regexp.MustCompile(`tflint-ignore: ([^\n*/#]+)`)
70+
71+
// LineAnnotation is an annotation for ignoring issues in a line
72+
type LineAnnotation struct {
73+
Content string
74+
Token hclsyntax.Token
75+
}
76+
5177
// IsAffected checks if the passed issue is affected with the annotation
52-
func (a *Annotation) IsAffected(issue *Issue) bool {
78+
func (a *LineAnnotation) IsAffected(issue *Issue) bool {
5379
if a.Token.Range.Filename != issue.Range.Filename {
5480
return false
5581
}
@@ -71,6 +97,36 @@ func (a *Annotation) IsAffected(issue *Issue) bool {
7197
}
7298

7399
// String returns the string representation of the annotation
74-
func (a *Annotation) String() string {
75-
return fmt.Sprintf("annotation:%s (%s)", a.Content, a.Token.Range.String())
100+
func (a *LineAnnotation) String() string {
101+
return fmt.Sprintf("tflint-ignore: %s (%s)", a.Content, a.Token.Range.String())
102+
}
103+
104+
var fileAnnotationPattern = regexp.MustCompile(`tflint-ignore-file: ([^\n*/#]+)`)
105+
106+
// FileAnnotation is an annotation for ignoring issues in a file
107+
type FileAnnotation struct {
108+
Content string
109+
Token hclsyntax.Token
110+
}
111+
112+
// IsAffected checks if the passed issue is affected with the annotation
113+
func (a *FileAnnotation) IsAffected(issue *Issue) bool {
114+
if a.Token.Range.Filename != issue.Range.Filename {
115+
return false
116+
}
117+
118+
rules := strings.Split(a.Content, ",")
119+
for i, rule := range rules {
120+
rules[i] = strings.TrimSpace(rule)
121+
}
122+
123+
if slices.Contains(rules, issue.Rule.Name()) || slices.Contains(rules, "all") {
124+
return true
125+
}
126+
return false
127+
}
128+
129+
// String returns the string representation of the annotation
130+
func (a *FileAnnotation) String() string {
131+
return fmt.Sprintf("tflint-ignore-file: %s (%s)", a.Content, a.Token.Range.String())
76132
}

0 commit comments

Comments
 (0)