github.com/vanstinator/golangci-lint@v0.0.0-20240223191551-cc572f00d9d1/pkg/config/issues.go (about) 1 package config 2 3 import ( 4 "fmt" 5 "regexp" 6 ) 7 8 const excludeRuleMinConditionsCount = 2 9 10 var DefaultExcludePatterns = []ExcludePattern{ 11 { 12 ID: "EXC0001", 13 Pattern: "Error return value of .((os\\.)?std(out|err)\\..*|.*Close" + 14 "|.*Flush|os\\.Remove(All)?|.*print(f|ln)?|os\\.(Un)?Setenv). is not checked", 15 Linter: "errcheck", 16 Why: "Almost all programs ignore errors on these functions and in most cases it's ok", 17 }, 18 { 19 ID: "EXC0002", 20 Pattern: "(comment on exported (method|function|type|const)|" + 21 "should have( a package)? comment|comment should be of the form)", 22 Linter: "golint", 23 Why: "Annoying issue about not having a comment. The rare codebase has such comments", 24 }, 25 { 26 ID: "EXC0003", 27 Pattern: "func name will be used as test\\.Test.* by other packages, and that stutters; consider calling this", 28 Linter: "golint", 29 Why: "False positive when tests are defined in package 'test'", 30 }, 31 { 32 ID: "EXC0004", 33 Pattern: "(possible misuse of unsafe.Pointer|should have signature)", 34 Linter: "govet", 35 Why: "Common false positives", 36 }, 37 { 38 ID: "EXC0005", 39 Pattern: "ineffective break statement. Did you mean to break out of the outer loop", 40 Linter: "staticcheck", 41 Why: "Developers tend to write in C-style with an explicit 'break' in a 'switch', so it's ok to ignore", 42 }, 43 { 44 ID: "EXC0006", 45 Pattern: "Use of unsafe calls should be audited", 46 Linter: "gosec", 47 Why: "Too many false-positives on 'unsafe' usage", 48 }, 49 { 50 ID: "EXC0007", 51 Pattern: "Subprocess launch(ed with variable|ing should be audited)", 52 Linter: "gosec", 53 Why: "Too many false-positives for parametrized shell calls", 54 }, 55 { 56 ID: "EXC0008", 57 Pattern: "(G104)", 58 Linter: "gosec", 59 Why: "Duplicated errcheck checks", 60 }, 61 { 62 ID: "EXC0009", 63 Pattern: "(Expect directory permissions to be 0750 or less|Expect file permissions to be 0600 or less)", 64 Linter: "gosec", 65 Why: "Too many issues in popular repos", 66 }, 67 { 68 ID: "EXC0010", 69 Pattern: "Potential file inclusion via variable", 70 Linter: "gosec", 71 Why: "False positive is triggered by 'src, err := ioutil.ReadFile(filename)'", 72 }, 73 { 74 ID: "EXC0011", 75 Pattern: "(comment on exported (method|function|type|const)|" + 76 "should have( a package)? comment|comment should be of the form)", 77 Linter: "stylecheck", 78 Why: "Annoying issue about not having a comment. The rare codebase has such comments", 79 }, 80 { 81 ID: "EXC0012", 82 Pattern: `exported (.+) should have comment( \(or a comment on this block\))? or be unexported`, 83 Linter: "revive", 84 Why: "Annoying issue about not having a comment. The rare codebase has such comments", 85 }, 86 { 87 ID: "EXC0013", 88 Pattern: `package comment should be of the form "(.+)...`, 89 Linter: "revive", 90 Why: "Annoying issue about not having a comment. The rare codebase has such comments", 91 }, 92 { 93 ID: "EXC0014", 94 Pattern: `comment on exported (.+) should be of the form "(.+)..."`, 95 Linter: "revive", 96 Why: "Annoying issue about not having a comment. The rare codebase has such comments", 97 }, 98 { 99 ID: "EXC0015", 100 Pattern: `should have a package comment`, 101 Linter: "revive", 102 Why: "Annoying issue about not having a comment. The rare codebase has such comments", 103 }, 104 } 105 106 type Issues struct { 107 IncludeDefaultExcludes []string `mapstructure:"include"` 108 ExcludeCaseSensitive bool `mapstructure:"exclude-case-sensitive"` 109 ExcludePatterns []string `mapstructure:"exclude"` 110 ExcludeRules []ExcludeRule `mapstructure:"exclude-rules"` 111 UseDefaultExcludes bool `mapstructure:"exclude-use-default"` 112 113 MaxIssuesPerLinter int `mapstructure:"max-issues-per-linter"` 114 MaxSameIssues int `mapstructure:"max-same-issues"` 115 116 DiffFromRevision string `mapstructure:"new-from-rev"` 117 DiffPatchFilePath string `mapstructure:"new-from-patch"` 118 WholeFiles bool `mapstructure:"whole-files"` 119 Diff bool `mapstructure:"new"` 120 121 NeedFix bool `mapstructure:"fix"` 122 } 123 124 type ExcludeRule struct { 125 BaseRule `mapstructure:",squash"` 126 } 127 128 func (e *ExcludeRule) Validate() error { 129 return e.BaseRule.Validate(excludeRuleMinConditionsCount) 130 } 131 132 type BaseRule struct { 133 Linters []string 134 Path string 135 PathExcept string `mapstructure:"path-except"` 136 Text string 137 Source string 138 } 139 140 func (b *BaseRule) Validate(minConditionsCount int) error { 141 if err := validateOptionalRegex(b.Path); err != nil { 142 return fmt.Errorf("invalid path regex: %w", err) 143 } 144 if err := validateOptionalRegex(b.PathExcept); err != nil { 145 return fmt.Errorf("invalid path-except regex: %w", err) 146 } 147 if err := validateOptionalRegex(b.Text); err != nil { 148 return fmt.Errorf("invalid text regex: %w", err) 149 } 150 if err := validateOptionalRegex(b.Source); err != nil { 151 return fmt.Errorf("invalid source regex: %w", err) 152 } 153 nonBlank := 0 154 if len(b.Linters) > 0 { 155 nonBlank++ 156 } 157 // Filtering by path counts as one condition, regardless how it is done (one or both). 158 // Otherwise, a rule with Path and PathExcept set would pass validation 159 // whereas before the introduction of path-except that wouldn't have been precise enough. 160 if b.Path != "" || b.PathExcept != "" { 161 nonBlank++ 162 } 163 if b.Text != "" { 164 nonBlank++ 165 } 166 if b.Source != "" { 167 nonBlank++ 168 } 169 if nonBlank < minConditionsCount { 170 return fmt.Errorf("at least %d of (text, source, path[-except], linters) should be set", minConditionsCount) 171 } 172 return nil 173 } 174 175 func validateOptionalRegex(value string) error { 176 if value == "" { 177 return nil 178 } 179 _, err := regexp.Compile(value) 180 return err 181 } 182 183 type ExcludePattern struct { 184 ID string 185 Pattern string 186 Linter string 187 Why string 188 } 189 190 func GetDefaultExcludePatternsStrings() []string { 191 ret := make([]string, len(DefaultExcludePatterns)) 192 for i, p := range DefaultExcludePatterns { 193 ret[i] = p.Pattern 194 } 195 return ret 196 } 197 198 // TODO(ldez): this behavior must be changed in v2, because this is confusing. 199 func GetExcludePatterns(include []string) []ExcludePattern { 200 includeMap := make(map[string]struct{}, len(include)) 201 for _, inc := range include { 202 includeMap[inc] = struct{}{} 203 } 204 205 var ret []ExcludePattern 206 for _, p := range DefaultExcludePatterns { 207 if _, ok := includeMap[p.ID]; !ok { 208 ret = append(ret, p) 209 } 210 } 211 212 return ret 213 }