Repository: mgechev/revive
Pull request #1077 (2024-10-25 10:12:26)

fix: change "GO" to "Go" in the use-any rule
The PR changes the failure message for the `use-any` rule from `since GO 1.18 'interface{}' can be replaced by 'any'` to `since Go 1.18 'interface{}' can be replaced by 'any'`.

See ["Is the language called Go or Golang?"](https://go.dev/doc/faq#go_or_golang)


Patch:
diff --git a/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md
similarity index 92%
rename from PULL_REQUEST_TEMPLATE.md
rename to .github/PULL_REQUEST_TEMPLATE.md
index 4adec92..80c267c 100644
--- a/PULL_REQUEST_TEMPLATE.md
+++ b/.github/PULL_REQUEST_TEMPLATE.md
@@ -6,7 +6,7 @@
 <!-- Please, describe in details what's your motivation for this PR -->
 <!-- Did you add tests? -->
 <!-- Does your code follow the coding style of the rest of the repository? -->
-<!-- Does the Travis build passes? -->
+<!-- Does the GitHub Action build passes? -->
 
 <!-- ### FOOTER (OPTIONAL) ### -->
 <!-- If you're closing an issue, add "Closes #XXXX" in your comment. This way, the PR will be linked to the issue automatically. -->
diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml
index 751d8ee..6630591 100644
--- a/.github/workflows/build.yaml
+++ b/.github/workflows/build.yaml
@@ -9,6 +9,6 @@ jobs:
     name: Build
     runs-on: ubuntu-latest
     steps:
-      - uses: actions/checkout@v2
+      - uses: actions/checkout@v4
       - name: build
         run: make build
diff --git a/.github/workflows/lint.yaml b/.github/workflows/lint.yaml
index b6b6d0b..850c70c 100644
--- a/.github/workflows/lint.yaml
+++ b/.github/workflows/lint.yaml
@@ -12,7 +12,9 @@ jobs:
     steps:
 
     - name: Check out code into the Go module directory
-      uses: actions/checkout@v2
+      uses: actions/checkout@v4
 
     - name: Run Revive Action
       uses: morphy2k/revive-action@v2
+      with:
+        config: revive.toml
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index f0c75e0..7182a26 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -11,17 +11,17 @@ jobs:
     steps:
       -
         name: Checkout
-        uses: actions/checkout@v2
+        uses: actions/checkout@v4
         with:
           fetch-depth: 0
       -
         name: Set up Go
-        uses: actions/setup-go@v2
+        uses: actions/setup-go@v5
         with:
-          go-version: 1.22
+          go-version: stable
       -
         name: Run GoReleaser
-        uses: goreleaser/goreleaser-action@v2
+        uses: goreleaser/goreleaser-action@v6
         with:
           version: latest
           args: release --clean
diff --git a/Dockerfile b/Dockerfile
index 2b59169..7d85fa6 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -1,4 +1,4 @@
-FROM --platform=$BUILDPLATFORM golang:1.22 AS build
+FROM --platform=$BUILDPLATFORM golang:1.23 AS build
 
 ARG VERSION
 ARG REVISION
diff --git a/README.md b/README.md
index 051d789..a661e31 100644
--- a/README.md
+++ b/README.md
@@ -66,6 +66,7 @@ Here's how `revive` is different from `golint`:
 <!-- TOC -->
 
 - [revive](#revive)
+  - [Who uses Revive](#who-uses-revive)
   - [Installation](#installation)
   - [Usage](#usage)
     - [Docker](#docker)
@@ -73,7 +74,7 @@ Here's how `revive` is different from `golint`:
     - [Text Editors](#text-editors)
     - [GitHub Actions](#github-actions)
     - [Continuous Integration](#continuous-integration)
-    - [Linter Aggregators](#linter-aggregators)
+    - [Linter aggregators](#linter-aggregators)
       - [golangci-lint](#golangci-lint)
     - [Command Line Flags](#command-line-flags)
     - [Sample Invocations](#sample-invocations)
@@ -82,6 +83,7 @@ Here's how `revive` is different from `golint`:
     - [Default Configuration](#default-configuration)
     - [Custom Configuration](#custom-configuration)
     - [Recommended Configuration](#recommended-configuration)
+    - [Rule-level file excludes](#rule-level-file-excludes)
   - [Available Rules](#available-rules)
   - [Configurable rules](#configurable-rules)
     - [`var-naming`](#var-naming)
@@ -93,12 +95,13 @@ Here's how `revive` is different from `golint`:
     - [Unix](#unix)
     - [SARIF](#sarif)
   - [Extensibility](#extensibility)
-    - [Custom Rule](#writing-a-custom-rule)
+    - [Writing a Custom Rule](#writing-a-custom-rule)
       - [Example](#example)
+      - [Using `revive` as a library](#using-revive-as-a-library)
     - [Custom Formatter](#custom-formatter)
   - [Speed Comparison](#speed-comparison)
     - [golint](#golint)
-    - [revive](#revive)
+    - [revive's speed](#revives-speed)
   - [Overriding colorization detection](#overriding-colorization-detection)
   - [Contributors](#contributors)
   - [License](#license)
@@ -125,6 +128,7 @@ Since the default behavior of `revive` is compatible with `golint`, without prov
 `revive` supports a `-config` flag whose value should correspond to a TOML file describing which rules to use for `revive`'s linting. If not provided, `revive` will try to use a global config file (assumed to be located at `$HOME/revive.toml`). Otherwise, if no configuration TOML file is found then `revive` uses a built-in set of default linting rules.
 
 ### Docker
+
 A volume must be mounted to share the current repository with the container.
 Please refer to the [bind mounts Docker documentation](https://docs.docker.com/storage/bind-mounts/)
 
@@ -174,6 +178,7 @@ If you want to use revive with Bazel, look at the [rules](https://github.com/atl
 ### Linter aggregators
 
 #### golangci-lint
+
 To enable `revive` in `golangci-lint` you need to add `revive` to the list of enabled linters:
 
 ```yaml
@@ -209,7 +214,7 @@ Please notice that if no particular configuration is provided, `revive` will beh
 `revive` accepts the following command line parameters:
 
 - `-config [PATH]` - path to the config file in TOML format, defaults to `$HOME/revive.toml` if present.
-- `-exclude [PATTERN]` - pattern for files/directories/packages to be excluded for linting. You can specify the files you want to exclude for linting either as package name (i.e. `github.com/mgechev/revive`), list them as individual files (i.e. `file.go`), directories (i.e. `./foo/...`), or any combination of the three.
+- `-exclude [PATTERN]` - pattern for files/directories/packages to be excluded for linting. You can specify the files you want to exclude for linting either as package name (i.e. `github.com/mgechev/revive`), list them as individual files (i.e. `file.go`), directories (i.e. `./foo/...`), or any combination of the three. If no exclusion patterns are specified, `vendor/...` will be excluded by default.
 - `-formatter [NAME]` - formatter to be used for the output. The currently available formatters are:
 
   - `default` - will output the failures the same way that `golint` does.
@@ -266,6 +271,7 @@ You can document why you disable the linter by adding a trailing text in the dir
 ```go
 //revive:disable Until the code is stable
 ```
+
 ```go
 //revive:disable:cyclomatic High complexity score but easy to understand
 ```
@@ -332,6 +338,7 @@ For example:
 [rule.line-length-limit]
     Disabled = true
 ```
+
 When enabling all rules you still need/can provide specific configurations for rules.
 The following file is an example configuration where all rules are enabled, except for those that are explicitly disabled, and some rules are configured with particular arguments:
 
@@ -540,7 +547,8 @@ List of all available rules. The rules ported from `golint` are left unchanged a
 | [`enforce-repeated-arg-type-style`](./RULES_DESCRIPTIONS.md#enforce-repeated-arg-type-style) |  string (defaults to "any")  |  Enforces consistent style for repeated argument and/or return value types. |    no    |  no   |
 | [`max-control-nesting`](./RULES_DESCRIPTIONS.md#max-control-nesting) |  int (defaults to 5)  | Sets restriction for maximum nesting of control structures. |    no    |  no   |
 | [`comments-density`](./RULES_DESCRIPTIONS.md#comments-density) |  int (defaults to 0)  | Enforces a minimum comment / code relation |    no    |  no   |
-
+| [`file-length-limit`](./RULES_DESCRIPTIONS.md#file-length-limit) | map (optional)| Enforces a maximum number of lines per file |    no    |  no   |
+| [`filename-format`](./RULES_DESCRIPTIONS.md#filename-format) | regular expression (optional) | Enforces the formatting of filenames |   no    |  no   |
 
 ## Configurable rules
 
@@ -588,6 +596,7 @@ The unix formatter produces the same output as the default formatter but surroun
 ![Unix formatter](/assets/formatter-unix.png)
 
 ### SARIF
+
 The `sarif`  formatter produces outputs in SARIF, for _Static Analysis Results Interchange Format_, a standard JSON-based format for the output of static analysis tools defined and promoted by [OASIS](https://www.oasis-open.org/).
 
 Current supported version of the standard is [SARIF-v2.1.0](https://docs.oasis-open.org/sarif/sarif/v2.1.0/csprd01/sarif-v2.1.0-csprd01.html
@@ -631,6 +640,7 @@ With the snippet above we:
 A sample rule implementation can be found [here](/rule/argument-limit.go).
 
 #### Using `revive` as a library
+
 If a rule is specific to your use case
 (i.e. it is not a good candidate to be added to `revive`'s rule set) you can add it to your linter using `revive` as a linting engine.
 
@@ -740,7 +750,7 @@ user    0m57.844s
 sys     0m9.146s
 ```
 
-### revive
+### revive's speed
 
 ```shell
 # no type checking
diff --git a/RULES_DESCRIPTIONS.md b/RULES_DESCRIPTIONS.md
index 273c127..fcd4e68 100644
--- a/RULES_DESCRIPTIONS.md
+++ b/RULES_DESCRIPTIONS.md
@@ -38,6 +38,8 @@ List of all available rules.
   - [errorf](#errorf)
   - [exported](#exported)
   - [file-header](#file-header)
+  - [file-length-limit](#file-length-limit)
+  - [filename-format](#filename-format)
   - [flag-parameter](#flag-parameter)
   - [function-length](#function-length)
   - [function-result-limit](#function-result-limit)
@@ -501,6 +503,35 @@ Example:
   arguments = ["This is the text that must appear at the top of source files."]
 ```
 
+## file-length-limit
+
+_Description_: This rule enforces a maximum number of lines per file, in order to aid in maintainability and reduce complexity.
+
+_Configuration_:
+
+* `max` (int) a maximum number of lines in a file. Must be non-negative integers. 0 means the rule is disabled (default `0`);
+* `skipComments` (bool) if true ignore and do not count lines containing just comments (default `false`);
+* `skipBlankLines` (bool) if true ignore and do not count lines made up purely of whitespace (default `false`).
+
+Example:
+
+```toml
+[rule.file-length-limit]
+  arguments = [{max=100,skipComments=true,skipBlankLines=true}]
+```
+
+## filename-format
+_Description_: enforces conventions on source file names. By default, the rule enforces filenames of the form `^[_A-Za-z0-9][_A-Za-z0-9-]*.go$`: Optionally, the rule can be configured to enforce other forms.
+
+_Configuration_: (string) regular expression for source filenames.
+
+Example:
+
+```toml
+[rule.filename-format]
+    arguments=["^[_a-z][_a-z0-9]*.go$"]
+```
+
 ## flag-parameter
 
 _Description_: If a function controls the flow of another by passing it information on what to do, both functions are said to be [control-coupled](https://en.wikipedia.org/wiki/Coupling_(computer_programming)#Procedural_programming).
diff --git a/config/config.go b/config/config.go
index fed1d19..16559f5 100644
--- a/config/config.go
+++ b/config/config.go
@@ -96,6 +96,8 @@ var allRules = append([]lint.Rule{
 	&rule.EnforceSliceStyleRule{},
 	&rule.MaxControlNestingRule{},
 	&rule.CommentsDensityRule{},
+	&rule.FileLengthLimitRule{},
+	&rule.FilenameFormatRule{},
 }, defaultRules...)
 
 var allFormatters = []lint.Formatter{
diff --git a/lint/package.go b/lint/package.go
index 441e0f4..2ab035f 100644
--- a/lint/package.go
+++ b/lint/package.go
@@ -166,17 +166,17 @@ func (p *Package) scanSortable() {
 
 	// bitfield for which methods exist on each type.
 	const (
-		Len = 1 << iota
-		Less
-		Swap
+		bfLen = 1 << iota
+		bfLess
+		bfSwap
 	)
-	nmap := map[string]int{"Len": Len, "Less": Less, "Swap": Swap}
+	nmap := map[string]int{"Len": bfLen, "Less": bfLess, "Swap": bfSwap}
 	has := make(map[string]int)
 	for _, f := range p.files {
 		ast.Walk(&walker{nmap, has}, f.AST)
 	}
 	for typ, ms := range has {
-		if ms == Len|Less|Swap {
+		if ms == bfLen|bfLess|bfSwap {
 			p.sortable[typ] = true
 		}
 	}
diff --git a/revive.toml b/revive.toml
new file mode 100644
index 0000000..3d47522
--- /dev/null
+++ b/revive.toml
@@ -0,0 +1,38 @@
+# This configuration for the revive linter used for linting the revive's codebase itself.
+# See .github/workflows/lint.yaml.
+
+ignoreGeneratedHeader = false
+severity = "warning"
+confidence = 0.8
+errorCode = 1
+warningCode = 1
+
+[rule.bare-return]
+[rule.blank-imports]
+[rule.context-as-argument]
+[rule.context-keys-type]
+[rule.dot-imports]
+[rule.empty-block]
+[rule.empty-lines]
+[rule.error-naming]
+[rule.error-return]
+[rule.error-strings]
+[rule.errorf]
+[rule.exported]
+[rule.increment-decrement]
+[rule.indent-error-flow]
+[rule.line-length-limit]
+    arguments = [200]
+[rule.package-comments]
+[rule.range]
+[rule.receiver-naming]
+[rule.redefines-builtin-id]
+[rule.superfluous-else]
+[rule.time-naming]
+[rule.unexported-naming]
+[rule.unexported-return]
+[rule.unreachable-code]
+[rule.unused-parameter]
+[rule.useless-break]
+[rule.var-declaration]
+[rule.var-naming]
diff --git a/revivelib/core.go b/revivelib/core.go
index 4e076e9..31463fe 100644
--- a/revivelib/core.go
+++ b/revivelib/core.go
@@ -82,7 +82,7 @@ func (r *Revive) Lint(patterns ...*LintPattern) (<-chan lint.Failure, error) {
 	if len(excludePatterns) == 0 { // if no excludes were set
 		excludePatterns = r.config.Exclude // use those from the configuration
 	}
-	
+
 	// by default if no excludes exclude vendor
 	if len(excludePatterns) == 0 {
 		excludePatterns = []string{"vendor/..."}
@@ -95,7 +95,6 @@ func (r *Revive) Lint(patterns ...*LintPattern) (<-chan lint.Failure, error) {
 
 	revive := lint.New(func(file string) ([]byte, error) {
 		contents, err := os.ReadFile(file)
-
 		if err != nil {
 			return nil, fmt.Errorf("reading file %v: %w", file, err)
 		}
diff --git a/rule/add-constant.go b/rule/add-constant.go
index f7a2447..233f1d8 100644
--- a/rule/add-constant.go
+++ b/rule/add-constant.go
@@ -160,14 +160,14 @@ func (w *lintAddConstantRule) isIgnoredFunc(fName string) bool {
 }
 
 func (w *lintAddConstantRule) checkStrLit(n *ast.BasicLit) {
-	const IgnoreMarker = -1
+	const ignoreMarker = -1
 
 	if w.allowList[kindSTRING][n.Value] {
 		return
 	}
 
 	count := w.strLits[n.Value]
-	mustCheck := count > IgnoreMarker
+	mustCheck := count > ignoreMarker
 	if mustCheck {
 		w.strLits[n.Value] = count + 1
 		if w.strLits[n.Value] > w.strLitLimit {
diff --git a/rule/comments-density.go b/rule/comments-density.go
index 5956fea..c5298ea 100644
--- a/rule/comments-density.go
+++ b/rule/comments-density.go
@@ -53,7 +53,8 @@ func (r *CommentsDensityRule) Apply(file *lint.File, arguments lint.Arguments) [
 			{
 				Node:       file.AST,
 				Confidence: 1,
-				Failure:    fmt.Sprintf("the file has a comment density of %2.f%% (%d comment lines for %d code lines) but expected a minimum of %d%%", density, commentsLines, statementsCount, r.minimumCommentsDensity),
+				Failure: fmt.Sprintf("the file has a comment density of %2.f%% (%d comment lines for %d code lines) but expected a minimum of %d%%",
+					density, commentsLines, statementsCount, r.minimumCommentsDensity),
 			},
 		}
 	}
diff --git a/rule/dot-imports.go b/rule/dot-imports.go
index 523b481..df0b2a7 100644
--- a/rule/dot-imports.go
+++ b/rule/dot-imports.go
@@ -59,17 +59,17 @@ func (r *DotImportsRule) configure(arguments lint.Arguments) {
 	}
 
 	if allowedPkgArg, ok := args["allowedPackages"]; ok {
-		if pkgs, ok := allowedPkgArg.([]any); ok {
-			for _, p := range pkgs {
-				if pkg, ok := p.(string); ok {
-					r.allowedPackages.add(pkg)
-				} else {
-					panic(fmt.Sprintf("Invalid argument to the dot-imports rule, string expected. Got '%v' (%T)", p, p))
-				}
-			}
-		} else {
+		pkgs, ok := allowedPkgArg.([]any)
+		if !ok {
 			panic(fmt.Sprintf("Invalid argument to the dot-imports rule, []string expected. Got '%v' (%T)", allowedPkgArg, allowedPkgArg))
 		}
+		for _, p := range pkgs {
+			pkg, ok := p.(string)
+			if !ok {
+				panic(fmt.Sprintf("Invalid argument to the dot-imports rule, string expected. Got '%v' (%T)", p, p))
+			}
+			r.allowedPackages.add(pkg)
+		}
 	}
 }
 
diff --git a/rule/early-return.go b/rule/early-return.go
index 9c04a1d..62d491f 100644
--- a/rule/early-return.go
+++ b/rule/early-return.go
@@ -21,27 +21,27 @@ func (*EarlyReturnRule) Name() string {
 	return "early-return"
 }
 
-// CheckIfElse evaluates the rule against an ifelse.Chain.
-func (*EarlyReturnRule) CheckIfElse(chain ifelse.Chain, args ifelse.Args) (failMsg string) {
+// CheckIfElse evaluates the rule against an ifelse.Chain and returns a failure message if applicable.
+func (*EarlyReturnRule) CheckIfElse(chain ifelse.Chain, args ifelse.Args) string {
 	if !chain.Else.Deviates() {
 		// this rule only applies if the else-block deviates control flow
-		return
+		return ""
 	}
 
 	if chain.HasPriorNonDeviating && !chain.If.IsEmpty() {
 		// if we de-indent this block then a previous branch
 		// might flow into it, affecting program behaviour
-		return
+		return ""
 	}
 
 	if chain.If.Deviates() {
 		// avoid overlapping with superfluous-else
-		return
+		return ""
 	}
 
 	if args.PreserveScope && !chain.AtBlockEnd && (chain.HasInitializer || chain.If.HasDecls) {
 		// avoid increasing variable scope
-		return
+		return ""
 	}
 
 	if chain.If.IsEmpty() {
diff --git a/rule/enforce-map-style.go b/rule/enforce-map-style.go
index 1c4717c..c698c40 100644
--- a/rule/enforce-map-style.go
+++ b/rule/enforce-map-style.go
@@ -65,7 +65,6 @@ func (r *EnforceMapStyleRule) configure(arguments lint.Arguments) {
 
 	var err error
 	r.enforceMapStyle, err = mapStyleFromString(enforceMapStyle)
-
 	if err != nil {
 		panic(fmt.Sprintf("Invalid argument to the enforce-map-style rule: %v", err))
 	}
diff --git a/rule/enforce-slice-style.go b/rule/enforce-slice-style.go
index df72c8c..14be258 100644
--- a/rule/enforce-slice-style.go
+++ b/rule/enforce-slice-style.go
@@ -69,7 +69,6 @@ func (r *EnforceSliceStyleRule) configure(arguments lint.Arguments) {
 
 	var err error
 	r.enforceSliceStyle, err = sliceStyleFromString(enforceSliceStyle)
-
 	if err != nil {
 		panic(fmt.Sprintf("Invalid argument to the enforce-slice-style rule: %v", err))
 	}
diff --git a/rule/file-length-limit.go b/rule/file-length-limit.go
new file mode 100644
index 0000000..c5a5641
--- /dev/null
+++ b/rule/file-length-limit.go
@@ -0,0 +1,138 @@
+package rule
+
+import (
+	"bufio"
+	"bytes"
+	"fmt"
+	"go/ast"
+	"go/token"
+	"strings"
+	"sync"
+
+	"github.com/mgechev/revive/lint"
+)
+
+// FileLengthLimitRule lints the number of lines in a file.
+type FileLengthLimitRule struct {
+	// max is the maximum number of lines allowed in a file. 0 means the rule is disabled.
+	max int
+	// skipComments indicates whether to skip comment lines when counting lines.
+	skipComments bool
+	// skipBlankLines indicates whether to skip blank lines when counting lines.
+	skipBlankLines bool
+	sync.Mutex
+}
+
+// Apply applies the rule to given file.
+func (r *FileLengthLimitRule) Apply(file *lint.File, arguments lint.Arguments) []lint.Failure {
+	r.configure(arguments)
+
+	if r.max <= 0 {
+		// when max is negative or 0 the rule is disabled
+		return nil
+	}
+
+	all := 0
+	blank := 0
+	scanner := bufio.NewScanner(bytes.NewReader(file.Content()))
+	for scanner.Scan() {
+		all++
+		if len(bytes.TrimSpace(scanner.Bytes())) == 0 {
+			blank++
+		}
+	}
+
+	if err := scanner.Err(); err != nil {
+		panic(err.Error())
+	}
+
+	lines := all
+	if r.skipComments {
+		lines -= countCommentLines(file.AST.Comments)
+	}
+
+	if r.skipBlankLines {
+		lines -= blank
+	}
+
+	if lines <= r.max {
+		return nil
+	}
+
+	return []lint.Failure{
+		{
+			Category:   "code-style",
+			Confidence: 1,
+			Position: lint.FailurePosition{
+				Start: token.Position{
+					Filename: file.Name,
+					Line:     all,
+				},
+			},
+			Failure: fmt.Sprintf("file length is %d lines, which exceeds the limit of %d", lines, r.max),
+		},
+	}
+}
+
+func (r *FileLengthLimitRule) configure(arguments lint.Arguments) {
+	r.Lock()
+	defer r.Unlock()
+
+	if r.max != 0 {
+		return // already configured
+	}
+
+	if len(arguments) < 1 {
+		return // use default
+	}
+
+	argKV, ok := arguments[0].(map[string]any)
+	if !ok {
+		panic(fmt.Sprintf(`invalid argument to the "file-length-limit" rule. Expecting a k,v map, got %T`, arguments[0]))
+	}
+	for k, v := range argKV {
+		switch k {
+		case "max":
+			maxLines, ok := v.(int64)
+			if !ok || maxLines < 0 {
+				panic(fmt.Sprintf(`invalid configuration value for max lines in "file-length-limit" rule; need positive int64 but got %T`, arguments[0]))
+			}
+			r.max = int(maxLines)
+		case "skipComments":
+			skipComments, ok := v.(bool)
+			if !ok {
+				panic(fmt.Sprintf(`invalid configuration value for skip comments in "file-length-limit" rule; need bool but got %T`, arguments[1]))
+			}
+			r.skipComments = skipComments
+		case "skipBlankLines":
+			skipBlankLines, ok := v.(bool)
+			if !ok {
+				panic(fmt.Sprintf(`invalid configuration value for skip blank lines in "file-length-limit" rule; need bool but got %T`, arguments[2]))
+			}
+			r.skipBlankLines = skipBlankLines
+		}
+	}
+}
+
+// Name returns the rule name.
+func (*FileLengthLimitRule) Name() string {
+	return "file-length-limit"
+}
+
+func countCommentLines(comments []*ast.CommentGroup) int {
+	count := 0
+	for _, cg := range comments {
+		for _, comment := range cg.List {
+			if len(comment.Text) < 2 {
+				continue
+			}
+			switch comment.Text[1] {
+			case '/': // single-line comment
+				count++
+			case '*': // multi-line comment
+				count += strings.Count(comment.Text, "\n") + 1
+			}
+		}
+	}
+	return count
+}
diff --git a/rule/filename-format.go b/rule/filename-format.go
new file mode 100644
index 0000000..49fdf9c
--- /dev/null
+++ b/rule/filename-format.go
@@ -0,0 +1,87 @@
+package rule
+
+import (
+	"fmt"
+	"path/filepath"
+	"regexp"
+	"sync"
+	"unicode"
+
+	"github.com/mgechev/revive/lint"
+)
+
+// FilenameFormatRule lints source filenames according to a set of regular expressions given as arguments
+type FilenameFormatRule struct {
+	format *regexp.Regexp
+	sync.Mutex
+}
+
+// Apply applies the rule to the given file.
+func (r *FilenameFormatRule) Apply(file *lint.File, arguments lint.Arguments) []lint.Failure {
+	r.configure(arguments)
+
+	filename := filepath.Base(file.Name)
+	if r.format.MatchString(filename) {
+		return nil
+	}
+
+	failureMsg := fmt.Sprintf("Filename %s is not of the format %s.%s", filename, r.format.String(), r.getMsgForNonASCIIChars(filename))
+	return []lint.Failure{{
+		Confidence: 1,
+		Failure:    failureMsg,
+		RuleName:   r.Name(),
+		Node:       file.AST.Name,
+	}}
+}
+
+func (r *FilenameFormatRule) getMsgForNonASCIIChars(str string) string {
+	result := ""
+	for _, c := range str {
+		if c <= unicode.MaxASCII {
+			continue
+		}
+
+		result += fmt.Sprintf(" Non ASCII character %c (%U) found.", c, c)
+	}
+
+	return result
+}
+
+// Name returns the rule name.
+func (*FilenameFormatRule) Name() string {
+	return "filename-format"
+}
+
+var defaultFormat = regexp.MustCompile("^[_A-Za-z0-9][_A-Za-z0-9-]*.go$")
+
+func (r *FilenameFormatRule) configure(arguments lint.Arguments) {
+	r.Lock()
+	defer r.Unlock()
+
+	if r.format != nil {
+		return
+	}
+
+	argsCount := len(arguments)
+	if argsCount == 0 {
+		r.format = defaultFormat
+		return
+	}
+
+	if argsCount > 1 {
+		panic(fmt.Sprintf("rule %q expects only one argument, got %d %v", r.Name(), argsCount, arguments))
+	}
+
+	arg := arguments[0]
+	str, ok := arg.(string)
+	if !ok {
+		panic(fmt.Sprintf("rule %q expects a string argument, got %v of type %T", r.Name(), arg, arg))
+	}
+
+	format, err := regexp.Compile(str)
+	if err != nil {
+		panic(fmt.Sprintf("rule %q expects a valid regexp argument, got %v for %s", r.Name(), err, arg))
+	}
+
+	r.format = format
+}
diff --git a/rule/indent-error-flow.go b/rule/indent-error-flow.go
index 294ceef..ebc1e79 100644
--- a/rule/indent-error-flow.go
+++ b/rule/indent-error-flow.go
@@ -18,27 +18,27 @@ func (*IndentErrorFlowRule) Name() string {
 	return "indent-error-flow"
 }
 
-// CheckIfElse evaluates the rule against an ifelse.Chain.
-func (*IndentErrorFlowRule) CheckIfElse(chain ifelse.Chain, args ifelse.Args) (failMsg string) {
+// CheckIfElse evaluates the rule against an ifelse.Chain and returns a failure message if applicable.
+func (*IndentErrorFlowRule) CheckIfElse(chain ifelse.Chain, args ifelse.Args) string {
 	if !chain.If.Deviates() {
 		// this rule only applies if the if-block deviates control flow
-		return
+		return ""
 	}
 
 	if chain.HasPriorNonDeviating {
 		// if we de-indent the "else" block then a previous branch
 		// might flow into it, affecting program behaviour
-		return
+		return ""
 	}
 
 	if !chain.If.Returns() {
 		// avoid overlapping with superfluous-else
-		return
+		return ""
 	}
 
 	if args.PreserveScope && !chain.AtBlockEnd && (chain.HasInitializer || chain.Else.HasDecls) {
 		// avoid increasing variable scope
-		return
+		return ""
 	}
 
 	return "if block ends with a return statement, so drop this else and outdent its block"
diff --git a/rule/line-length-limit.go b/rule/line-length-limit.go
index 1e91646..a154b7a 100644
--- a/rule/line-length-limit.go
+++ b/rule/line-length-limit.go
@@ -38,7 +38,6 @@ func (r *LineLengthLimitRule) configure(arguments lint.Arguments) {
 	}
 
 	r.max = int(maxLength)
-
 }
 
 // Apply applies the rule to given file.
diff --git a/rule/max-public-structs.go b/rule/max-public-structs.go
index ea93402..70840e7 100644
--- a/rule/max-public-structs.go
+++ b/rule/max-public-structs.go
@@ -35,7 +35,6 @@ func (r *MaxPublicStructsRule) configure(arguments lint.Arguments) {
 		panic(`invalid value passed as argument number to the "max-public-structs" rule`)
 	}
 	r.max = maxStructs
-
 }
 
 // Apply applies the rule to given file.
diff --git a/rule/redefines-builtin-id.go b/rule/redefines-builtin-id.go
index 8aaaf03..10ea16a 100644
--- a/rule/redefines-builtin-id.go
+++ b/rule/redefines-builtin-id.go
@@ -163,7 +163,7 @@ func (w *lintRedefinesBuiltinID) Visit(node ast.Node) ast.Visitor {
 				if !isTypeOrName {
 					continue
 				}
-				
+
 				id := obj.Name
 				if ok, bt := w.isBuiltIn(id); ok {
 					w.addFailure(name, fmt.Sprintf("redefinition of the built-in %s %s", bt, id))
diff --git a/rule/superfluous-else.go b/rule/superfluous-else.go
index 2aa1b6b..18e8f3b 100644
--- a/rule/superfluous-else.go
+++ b/rule/superfluous-else.go
@@ -2,6 +2,7 @@ package rule
 
 import (
 	"fmt"
+
 	"github.com/mgechev/revive/internal/ifelse"
 	"github.com/mgechev/revive/lint"
 )
@@ -19,27 +20,27 @@ func (*SuperfluousElseRule) Name() string {
 	return "superfluous-else"
 }
 
-// CheckIfElse evaluates the rule against an ifelse.Chain.
-func (*SuperfluousElseRule) CheckIfElse(chain ifelse.Chain, args ifelse.Args) (failMsg string) {
+// CheckIfElse evaluates the rule against an ifelse.Chain and returns a failure message if applicable.
+func (*SuperfluousElseRule) CheckIfElse(chain ifelse.Chain, args ifelse.Args) string {
 	if !chain.If.Deviates() {
 		// this rule only applies if the if-block deviates control flow
-		return
+		return ""
 	}
 
 	if chain.HasPriorNonDeviating {
 		// if we de-indent the "else" block then a previous branch
 		// might flow into it, affecting program behaviour
-		return
+		return ""
 	}
 
 	if chain.If.Returns() {
 		// avoid overlapping with indent-error-flow
-		return
+		return ""
 	}
 
 	if args.PreserveScope && !chain.AtBlockEnd && (chain.HasInitializer || chain.Else.HasDecls) {
 		// avoid increasing variable scope
-		return
+		return ""
 	}
 
 	return fmt.Sprintf("if block ends with %v, so drop this else and outdent its block", chain.If.LongString())
diff --git a/rule/use-any.go b/rule/use-any.go
index bdf3c93..88160c2 100644
--- a/rule/use-any.go
+++ b/rule/use-any.go
@@ -47,7 +47,7 @@ func (w lintUseAny) Visit(n ast.Node) ast.Visitor {
 		Node:       n,
 		Confidence: 1,
 		Category:   "naming",
-		Failure:    "since GO 1.18 'interface{}' can be replaced by 'any'",
+		Failure:    "since Go 1.18 'interface{}' can be replaced by 'any'",
 	})
 
 	return w
diff --git a/rule/var-naming.go b/rule/var-naming.go
index e93159b..5a4d0dc 100644
--- a/rule/var-naming.go
+++ b/rule/var-naming.go
@@ -79,7 +79,6 @@ func (r *VarNamingRule) applyPackageCheckRules(walker *lintNames) {
 			Category:   "naming",
 		})
 	}
-
 }
 
 // Apply applies the rule to given file.
@@ -265,17 +264,17 @@ func (w *lintNames) Visit(n ast.Node) ast.Visitor {
 }
 
 func getList(arg any, argName string) []string {
-	temp, ok := arg.([]any)
+	args, ok := arg.([]any)
 	if !ok {
 		panic(fmt.Sprintf("Invalid argument to the var-naming rule. Expecting a %s of type slice with initialisms, got %T", argName, arg))
 	}
 	var list []string
-	for _, v := range temp {
-		if val, ok := v.(string); ok {
-			list = append(list, val)
-		} else {
+	for _, v := range args {
+		val, ok := v.(string)
+		if !ok {
 			panic(fmt.Sprintf("Invalid %s values of the var-naming rule. Expecting slice of strings but got element of type %T", val, arg))
 		}
+		list = append(list, val)
 	}
 	return list
 }
diff --git "a/testdata/filenam\320\265-with-non-ascii-char.go" "b/testdata/filenam\320\265-with-non-ascii-char.go"
new file mode 100644
index 0000000..a4c665e
--- /dev/null
+++ "b/testdata/filenam\320\265-with-non-ascii-char.go"
@@ -0,0 +1,3 @@
+package main
+
+// MATCH:1 /Filename filenamе-with-non-ascii-char.go is not of the format ^[_A-Za-z0-9][_A-Za-z0-9-]*.go$. Non ASCII character е (U+0435) found./


Test patch:
diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml
index c4a74b3..e47e6f3 100644
--- a/.github/workflows/test.yaml
+++ b/.github/workflows/test.yaml
@@ -12,16 +12,14 @@ jobs:
       fail-fast: false
       matrix:
         go-version:
-          - 1.21.x
-          - 1.22.x
+          - oldstable
+          - stable
     steps:
       - name: Checkout code
-        uses: actions/checkout@v3.0.2
+        uses: actions/checkout@v4
       - name: Set up Go
-        uses: actions/setup-go@v3.2.0
+        uses: actions/setup-go@v5
         with:
           go-version: ${{ matrix.go-version }}
-          cache: true
-          cache-dependency-path: '**/go.sum'
       - name: Run tests
         run: go test -race ./...
diff --git a/cli/main_test.go b/cli/main_test.go
index b2fabaf..be1f97d 100644
--- a/cli/main_test.go
+++ b/cli/main_test.go
@@ -40,7 +40,6 @@ func TestXDGConfigDirIsPrefferedFirst(t *testing.T) {
 }
 
 func TestHomeConfigDir(t *testing.T) {
-
 	homeDirPath := "/tmp-iofs/home/tester"
 	AppFs.MkdirAll(homeDirPath, 0755)
 
diff --git a/lint/filefilter_test.go b/lint/filefilter_test.go
index 6c20afd..11da313 100644
--- a/lint/filefilter_test.go
+++ b/lint/filefilter_test.go
@@ -95,7 +95,6 @@ func TestFileFilter(t *testing.T) {
 				t.Fatalf("should not match %s", fn)
 			}
 		}
-
 	})
 
 	t.Run("just *", func(t *testing.T) {
@@ -109,7 +108,6 @@ func TestFileFilter(t *testing.T) {
 				t.Fatalf("should match %s", fn)
 			}
 		}
-
 	})
 
 	t.Run("just ~", func(t *testing.T) {
@@ -123,6 +121,5 @@ func TestFileFilter(t *testing.T) {
 				t.Fatalf("should match %s", fn)
 			}
 		}
-
 	})
 }
diff --git a/lint/linter_test.go b/lint/linter_test.go
new file mode 100644
index 0000000..4c1f95d
--- /dev/null
+++ b/lint/linter_test.go
@@ -0,0 +1,31 @@
+package lint
+
+import "testing"
+
+// TestIsGenerated tests isGenerated function.
+func TestIsGenerated(t *testing.T) { //revive:disable-line:exported
+	tests := []struct {
+		source    string
+		generated bool
+	}{
+		{"// Code Generated by some tool. DO NOT EDIT.", false},
+		{"// Code generated by some tool. DO NOT EDIT.", true},
+		{"// Code generated by some tool. DO NOT EDIT", false},
+		{"// Code generated  DO NOT EDIT.", true},
+		{"// Code generated DO NOT EDIT.", false},
+		{"\t\t// Code generated by some tool. DO NOT EDIT.\npackage foo\n", false},
+		{"// Code generated by some tool. DO NOT EDIT.\npackage foo\n", true},
+		{"package foo\n// Code generated by some tool. DO NOT EDIT.\ntype foo int\n", true},
+		{"package foo\n // Code generated by some tool. DO NOT EDIT.\ntype foo int\n", false},
+		{"package foo\n// Code generated by some tool. DO NOT EDIT. \ntype foo int\n", false},
+		{"package foo\ntype foo int\n// Code generated by some tool. DO NOT EDIT.\n", true},
+		{"package foo\ntype foo int\n// Code generated by some tool. DO NOT EDIT.", true},
+	}
+
+	for i, test := range tests {
+		got := isGenerated([]byte(test.source))
+		if got != test.generated {
+			t.Errorf("test %d, isGenerated() = %v, want %v", i, got, test.generated)
+		}
+	}
+}
diff --git a/lint/utils.go b/lint/name.go
similarity index 100%
rename from lint/utils.go
rename to lint/name.go
diff --git a/lint/name_test.go b/lint/name_test.go
new file mode 100644
index 0000000..5a9091b
--- /dev/null
+++ b/lint/name_test.go
@@ -0,0 +1,44 @@
+package lint
+
+import "testing"
+
+// TestName tests Name function
+func TestName(t *testing.T) { //revive:disable-line:exported
+	tests := []struct {
+		name, want string
+	}{
+		{"foo_bar", "fooBar"},
+		{"foo_bar_baz", "fooBarBaz"},
+		{"Foo_bar", "FooBar"},
+		{"foo_WiFi", "fooWiFi"},
+		{"id", "id"},
+		{"Id", "ID"},
+		{"foo_id", "fooID"},
+		{"fooId", "fooID"},
+		{"fooUid", "fooUID"},
+		{"idFoo", "idFoo"},
+		{"uidFoo", "uidFoo"},
+		{"midIdDle", "midIDDle"},
+		{"APIProxy", "APIProxy"},
+		{"ApiProxy", "APIProxy"},
+		{"apiProxy", "apiProxy"},
+		{"_Leading", "_Leading"},
+		{"___Leading", "_Leading"},
+		{"trailing_", "trailing"},
+		{"trailing___", "trailing"},
+		{"a_b", "aB"},
+		{"a__b", "aB"},
+		{"a___b", "aB"},
+		{"Rpc1150", "RPC1150"},
+		{"case3_1", "case3_1"},
+		{"case3__1", "case3_1"},
+		{"IEEE802_16bit", "IEEE802_16bit"},
+		{"IEEE802_16Bit", "IEEE802_16Bit"},
+	}
+	for _, test := range tests {
+		got := Name(test.name, nil, nil)
+		if got != test.want {
+			t.Errorf("Name(%q) = %q, want %q", test.name, got, test.want)
+		}
+	}
+}
diff --git a/revivelib/core_internal_test.go b/revivelib/core_internal_test.go
index 3de7643..ea1f779 100644
--- a/revivelib/core_internal_test.go
+++ b/revivelib/core_internal_test.go
@@ -62,7 +62,7 @@ func getMockRevive(t *testing.T) *Revive {
 		NewExtraRule(&mockRule{}, lint.RuleConfig{}),
 	)
 	if err != nil {
-		t.Fatal(err.Error())
+		t.Fatal(err)
 	}
 
 	return revive
diff --git a/revivelib/core_test.go b/revivelib/core_test.go
index 3e401f6..045711a 100644
--- a/revivelib/core_test.go
+++ b/revivelib/core_test.go
@@ -98,7 +98,7 @@ func getMockRevive(t *testing.T) *revivelib.Revive {
 		revivelib.NewExtraRule(&mockRule{}, lint.RuleConfig{}),
 	)
 	if err != nil {
-		t.Fatal(err.Error())
+		t.Fatal(err)
 	}
 
 	return revive
diff --git a/test/file-filter_test.go b/test/file-filter_test.go
index 7729a71..a73d311 100644
--- a/test/file-filter_test.go
+++ b/test/file-filter_test.go
@@ -7,14 +7,14 @@ import (
 )
 
 type TestFileFilterRule struct {
-	WasApplyed bool
+	WasApplied bool
 }
 
 var _ lint.Rule = (*TestFileFilterRule)(nil)
 
 func (*TestFileFilterRule) Name() string { return "test-file-filter" }
 func (tfr *TestFileFilterRule) Apply(*lint.File, lint.Arguments) []lint.Failure {
-	tfr.WasApplyed = true
+	tfr.WasApplied = true
 	return nil
 }
 
@@ -22,7 +22,7 @@ func TestFileExcludeFilterAtRuleLevel(t *testing.T) {
 	t.Run("is called if no excludes", func(t *testing.T) {
 		rule := &TestFileFilterRule{}
 		testRule(t, "file-to-exclude", rule, &lint.RuleConfig{})
-		if !rule.WasApplyed {
+		if !rule.WasApplied {
 			t.Fatal("should call rule if no excludes")
 		}
 	})
@@ -31,7 +31,7 @@ func TestFileExcludeFilterAtRuleLevel(t *testing.T) {
 		cfg := &lint.RuleConfig{Exclude: []string{"no-matched.go"}}
 		cfg.Initialize()
 		testRule(t, "file-to-exclude", rule, cfg)
-		if !rule.WasApplyed {
+		if !rule.WasApplied {
 			t.Fatal("should call rule if no excludes")
 		}
 	})
@@ -41,7 +41,7 @@ func TestFileExcludeFilterAtRuleLevel(t *testing.T) {
 		cfg := &lint.RuleConfig{Exclude: []string{"../testdata/file-to-exclude.go"}}
 		cfg.Initialize()
 		testRule(t, "file-to-exclude", rule, cfg)
-		if rule.WasApplyed {
+		if rule.WasApplied {
 			t.Fatal("should not call rule if excluded")
 		}
 	})
diff --git a/test/file-length-limit_test.go b/test/file-length-limit_test.go
new file mode 100644
index 0000000..c000daa
--- /dev/null
+++ b/test/file-length-limit_test.go
@@ -0,0 +1,32 @@
+package test
+
+import (
+	"testing"
+
+	"github.com/mgechev/revive/lint"
+	"github.com/mgechev/revive/rule"
+)
+
+func TestFileLengthLimit(t *testing.T) {
+	testRule(t, "file-length-limit-disabled", &rule.FileLengthLimitRule{}, &lint.RuleConfig{
+		Arguments: []any{},
+	})
+	testRule(t, "file-length-limit-disabled", &rule.FileLengthLimitRule{}, &lint.RuleConfig{
+		Arguments: []any{map[string]any{"max": int64(0)}},
+	})
+	testRule(t, "file-length-limit-disabled", &rule.FileLengthLimitRule{}, &lint.RuleConfig{
+		Arguments: []any{map[string]any{"skipComments": true, "skipBlankLines": true}},
+	})
+	testRule(t, "file-length-limit-9", &rule.FileLengthLimitRule{}, &lint.RuleConfig{
+		Arguments: []any{map[string]any{"max": int64(9)}},
+	})
+	testRule(t, "file-length-limit-7-skip-comments", &rule.FileLengthLimitRule{}, &lint.RuleConfig{
+		Arguments: []any{map[string]any{"max": int64(7), "skipComments": true}},
+	})
+	testRule(t, "file-length-limit-6-skip-blank", &rule.FileLengthLimitRule{}, &lint.RuleConfig{
+		Arguments: []any{map[string]any{"max": int64(6), "skipBlankLines": true}},
+	})
+	testRule(t, "file-length-limit-4-skip-comments-skip-blank", &rule.FileLengthLimitRule{}, &lint.RuleConfig{
+		Arguments: []any{map[string]any{"max": int64(4), "skipComments": true, "skipBlankLines": true}},
+	})
+}
diff --git a/test/filename-format_test.go b/test/filename-format_test.go
new file mode 100644
index 0000000..2f0ef76
--- /dev/null
+++ b/test/filename-format_test.go
@@ -0,0 +1,16 @@
+package test
+
+import (
+	"testing"
+
+	"github.com/mgechev/revive/lint"
+	"github.com/mgechev/revive/rule"
+)
+
+func TestLintFilenameFormat(t *testing.T) {
+	testRule(t, "filename-ok-default", &rule.FilenameFormatRule{}, &lint.RuleConfig{})
+
+	testRule(t, "filenamе-with-non-ascii-char", &rule.FilenameFormatRule{}, &lint.RuleConfig{})
+
+	testRule(t, "filename_with_underscores", &rule.FilenameFormatRule{}, &lint.RuleConfig{Arguments: []any{"^[A-Za-z][A-Za-z0-9]*.go$"}})
+}
diff --git a/test/json-data-format_test.go b/test/json-data-format_test.go
index 3914b31..41b154c 100644
--- a/test/json-data-format_test.go
+++ b/test/json-data-format_test.go
@@ -9,9 +9,8 @@ import (
 
 func TestJsonDataFormat(t *testing.T) {
 	testRule(t, "json-data-format-atomic", &rule.AtomicRule{})
-
 }
+
 func TestJsonDataFormatVarNaming(t *testing.T) {
 	testRule(t, "json-data-format-var-naming", &rule.VarNamingRule{}, &lint.RuleConfig{})
-
 }
diff --git a/test/utils_test.go b/test/utils_test.go
index ad36869..7cc5f25 100644
--- a/test/utils_test.go
+++ b/test/utils_test.go
@@ -1,8 +1,6 @@
 package test
 
 import (
-	"bufio"
-	"bytes"
 	"encoding/json"
 	"fmt"
 	"go/ast"
@@ -107,11 +105,9 @@ func assertFailures(t *testing.T, baseDir string, fi os.FileInfo, src []byte, ru
 				}
 
 				if in.Confidence > 0 {
-
 					if in.Confidence != p.Confidence {
 						t.Errorf("Lint failed at %s:%d; got confidence %f, want %f", fi.Name(), in.Line, p.Confidence, in.Confidence)
 					}
-
 				}
 
 				// remove this problem from ps
@@ -177,7 +173,6 @@ func parseInstructions(t *testing.T, filename string, src []byte) []instruction
 					t.Fatalf("At %v:%d: %v", filename, ln, err)
 				}
 				ins = append(ins, jsonInst)
-				break
 			case "classic":
 				match, err := extractPattern(line)
 				if err != nil {
@@ -202,9 +197,7 @@ func parseInstructions(t *testing.T, filename string, src []byte) []instruction
 					Match:       match,
 					Replacement: repl,
 				})
-				break
 			}
-
 		}
 	}
 	return ins
@@ -214,7 +207,7 @@ func extractInstructionFromJSON(line string, lineNumber int) (instruction, error
 	// Use the json.Unmarshal function to parse the JSON into the struct
 	var jsonInst JSONInstruction
 	if err := json.Unmarshal([]byte(line), &jsonInst); err != nil {
-		fmt.Println("Error parsing JSON:", err)
+		return instruction{}, fmt.Errorf("parsing json instruction: %w", err)
 	}
 
 	ins := instruction{
@@ -224,11 +217,9 @@ func extractInstructionFromJSON(line string, lineNumber int) (instruction, error
 		Line:       lineNumber,
 	}
 	return ins, nil
-
 }
 
 func extractDataMode(line string) string {
-
 	if strings.HasPrefix(line, "json") {
 		return "json"
 	}
@@ -237,7 +228,6 @@ func extractDataMode(line string) string {
 	}
 
 	return ""
-
 }
 
 func extractPattern(line string) (string, error) {
@@ -295,47 +285,6 @@ func TestLine(t *testing.T) { //revive:disable-line:exported
 	}
 }
 
-// TestLintName tests lint.Name function
-func TestLintName(t *testing.T) { //revive:disable-line:exported
-	tests := []struct {
-		name, want string
-	}{
-		{"foo_bar", "fooBar"},
-		{"foo_bar_baz", "fooBarBaz"},
-		{"Foo_bar", "FooBar"},
-		{"foo_WiFi", "fooWiFi"},
-		{"id", "id"},
-		{"Id", "ID"},
-		{"foo_id", "fooID"},
-		{"fooId", "fooID"},
-		{"fooUid", "fooUID"},
-		{"idFoo", "idFoo"},
-		{"uidFoo", "uidFoo"},
-		{"midIdDle", "midIDDle"},
-		{"APIProxy", "APIProxy"},
-		{"ApiProxy", "APIProxy"},
-		{"apiProxy", "apiProxy"},
-		{"_Leading", "_Leading"},
-		{"___Leading", "_Leading"},
-		{"trailing_", "trailing"},
-		{"trailing___", "trailing"},
-		{"a_b", "aB"},
-		{"a__b", "aB"},
-		{"a___b", "aB"},
-		{"Rpc1150", "RPC1150"},
-		{"case3_1", "case3_1"},
-		{"case3__1", "case3_1"},
-		{"IEEE802_16bit", "IEEE802_16bit"},
-		{"IEEE802_16Bit", "IEEE802_16Bit"},
-	}
-	for _, test := range tests {
-		got := lint.Name(test.name, nil, nil)
-		if got != test.want {
-			t.Errorf("lintName(%q) = %q, want %q", test.name, got, test.want)
-		}
-	}
-}
-
 // exportedType reports whether typ is an exported type.
 // It is imprecise, and will err on the side of returning true,
 // such as for composite types.
@@ -392,49 +341,3 @@ func TestExportedType(t *testing.T) { //revive:disable-line:exported
 		}
 	}
 }
-
-var (
-	genHdr = []byte("// Code generated ")
-	genFtr = []byte(" DO NOT EDIT.")
-)
-
-// isGenerated reports whether the source file is generated code
-// according the rules from https://golang.org/s/generatedcode.
-func isGenerated(src []byte) bool {
-	sc := bufio.NewScanner(bytes.NewReader(src))
-	for sc.Scan() {
-		b := sc.Bytes()
-		if bytes.HasPrefix(b, genHdr) && bytes.HasSuffix(b, genFtr) && len(b) >= len(genHdr)+len(genFtr) {
-			return true
-		}
-	}
-	return false
-}
-
-// TestIsGenerated tests isGenerated function
-func TestIsGenerated(t *testing.T) { //revive:disable-line:exported
-	tests := []struct {
-		source    string
-		generated bool
-	}{
-		{"// Code Generated by some tool. DO NOT EDIT.", false},
-		{"// Code generated by some tool. DO NOT EDIT.", true},
-		{"// Code generated by some tool. DO NOT EDIT", false},
-		{"// Code generated  DO NOT EDIT.", true},
-		{"// Code generated DO NOT EDIT.", false},
-		{"\t\t// Code generated by some tool. DO NOT EDIT.\npackage foo\n", false},
-		{"// Code generated by some tool. DO NOT EDIT.\npackage foo\n", true},
-		{"package foo\n// Code generated by some tool. DO NOT EDIT.\ntype foo int\n", true},
-		{"package foo\n // Code generated by some tool. DO NOT EDIT.\ntype foo int\n", false},
-		{"package foo\n// Code generated by some tool. DO NOT EDIT. \ntype foo int\n", false},
-		{"package foo\ntype foo int\n// Code generated by some tool. DO NOT EDIT.\n", true},
-		{"package foo\ntype foo int\n// Code generated by some tool. DO NOT EDIT.", true},
-	}
-
-	for i, test := range tests {
-		got := isGenerated([]byte(test.source))
-		if got != test.generated {
-			t.Errorf("test %d, isGenerated() = %v, want %v", i, got, test.generated)
-		}
-	}
-}
diff --git a/test/var-naming_test.go b/test/var-naming_test.go
index f554286..9347385 100644
--- a/test/var-naming_test.go
+++ b/test/var-naming_test.go
@@ -23,5 +23,4 @@ func TestVarNaming(t *testing.T) {
 	testRule(t, "var-naming_skipPackageNameChecks-true", &rule.VarNamingRule{}, &lint.RuleConfig{
 		Arguments: []any{[]any{}, []any{}, []any{map[string]any{"skipPackageNameChecks": true}}},
 	})
-
 }
diff --git a/testdata/file-length-limit-4-skip-comments-skip-blank.go b/testdata/file-length-limit-4-skip-comments-skip-blank.go
new file mode 100644
index 0000000..ce086d0
--- /dev/null
+++ b/testdata/file-length-limit-4-skip-comments-skip-blank.go
@@ -0,0 +1,10 @@
+package fixtures
+
+import "fmt"
+
+// Foo is a function.
+func Foo(a, b int) {
+	fmt.Println("Hello, world!")
+}
+
+// MATCH /file length is 5 lines, which exceeds the limit of 4/
diff --git a/testdata/file-length-limit-6-skip-blank.go b/testdata/file-length-limit-6-skip-blank.go
new file mode 100644
index 0000000..9e9641c
--- /dev/null
+++ b/testdata/file-length-limit-6-skip-blank.go
@@ -0,0 +1,10 @@
+package fixtures
+
+import "fmt"
+
+// Foo is a function.
+func Foo(a, b int) {
+	fmt.Println("Hello, world!")
+}
+
+// MATCH /file length is 7 lines, which exceeds the limit of 6/
diff --git a/testdata/file-length-limit-7-skip-comments.go b/testdata/file-length-limit-7-skip-comments.go
new file mode 100644
index 0000000..ef67590
--- /dev/null
+++ b/testdata/file-length-limit-7-skip-comments.go
@@ -0,0 +1,20 @@
+package fixtures
+
+import "fmt"
+
+// Foo is a function.
+func Foo(a, b int) {
+	// This
+	/* is
+	a
+	*/
+	// a comment.
+	fmt.Println("Hello, world!")
+	/*
+		This is
+		multiline
+		comment.
+	*/
+}
+
+// MATCH /file length is 8 lines, which exceeds the limit of 7/
diff --git a/testdata/file-length-limit-9.go b/testdata/file-length-limit-9.go
new file mode 100644
index 0000000..649a201
--- /dev/null
+++ b/testdata/file-length-limit-9.go
@@ -0,0 +1,10 @@
+package fixtures
+
+import "fmt"
+
+// Foo is a function.
+func Foo(a, b int) {
+	fmt.Println("Hello, world!")
+}
+
+// MATCH /file length is 10 lines, which exceeds the limit of 9/
diff --git a/testdata/file-length-limit-disabled.go b/testdata/file-length-limit-disabled.go
new file mode 100644
index 0000000..1066985
--- /dev/null
+++ b/testdata/file-length-limit-disabled.go
@@ -0,0 +1,8 @@
+package fixtures
+
+import "fmt"
+
+// Foo is a function.
+func Foo(a, b int) {
+	fmt.Println("Hello, world!")
+}
diff --git a/testdata/filename-ok-default.go b/testdata/filename-ok-default.go
new file mode 100644
index 0000000..06ab7d0
--- /dev/null
+++ b/testdata/filename-ok-default.go
@@ -0,0 +1,1 @@
+package main
diff --git a/testdata/filename_with_underscores.go b/testdata/filename_with_underscores.go
new file mode 100644
index 0000000..cbdc6b5
--- /dev/null
+++ b/testdata/filename_with_underscores.go
@@ -0,0 +1,3 @@
+package main
+
+// MATCH:1 /Filename filename_with_underscores.go is not of the format ^[A-Za-z][A-Za-z0-9]*.go$./
diff --git a/testdata/use-any.go b/testdata/use-any.go
index bc6e647..9891e84 100644
--- a/testdata/use-any.go
+++ b/testdata/use-any.go
@@ -1,19 +1,19 @@
 package pkg
 
-var i interface{} // MATCH /since GO 1.18 'interface{}' can be replaced by 'any'/
+var i interface{} // MATCH /since Go 1.18 'interface{}' can be replaced by 'any'/
 
-type t interface{}   // MATCH /since GO 1.18 'interface{}' can be replaced by 'any'/
-type a = interface{} // MATCH /since GO 1.18 'interface{}' can be replaced by 'any'/
+type t interface{}   // MATCH /since Go 1.18 'interface{}' can be replaced by 'any'/
+type a = interface{} // MATCH /since Go 1.18 'interface{}' can be replaced by 'any'/
 
-func any1(a interface{}) { // MATCH /since GO 1.18 'interface{}' can be replaced by 'any'/
-	m1 := map[interface{}]string{}     // MATCH /since GO 1.18 'interface{}' can be replaced by 'any'/
-	m2 := map[int]interface{}{}        // MATCH /since GO 1.18 'interface{}' can be replaced by 'any'/
-	a := []interface{}{}               // MATCH /since GO 1.18 'interface{}' can be replaced by 'any'/
-	m3 := make(map[int]interface{}, 1) // MATCH /since GO 1.18 'interface{}' can be replaced by 'any'/
-	a2 := make([]interface{}, 2)       // MATCH /since GO 1.18 'interface{}' can be replaced by 'any'/
+func any1(a interface{}) { // MATCH /since Go 1.18 'interface{}' can be replaced by 'any'/
+	m1 := map[interface{}]string{}     // MATCH /since Go 1.18 'interface{}' can be replaced by 'any'/
+	m2 := map[int]interface{}{}        // MATCH /since Go 1.18 'interface{}' can be replaced by 'any'/
+	a := []interface{}{}               // MATCH /since Go 1.18 'interface{}' can be replaced by 'any'/
+	m3 := make(map[int]interface{}, 1) // MATCH /since Go 1.18 'interface{}' can be replaced by 'any'/
+	a2 := make([]interface{}, 2)       // MATCH /since Go 1.18 'interface{}' can be replaced by 'any'/
 }
 
-func any2(a int) interface{} {} // MATCH /since GO 1.18 'interface{}' can be replaced by 'any'/
+func any2(a int) interface{} {} // MATCH /since Go 1.18 'interface{}' can be replaced by 'any'/
 
 var ni interface{ Close() }