Repository: mgechev/revive
Pull request #1324 (2025-04-25 09:08:00)

docs: format code snippets in markdown files
The PR fixes the formatting of code blocks in all Markdown files.md. It also adds [`mdsf`](https://mdsf.mhouge.dk/) to lint formatting in CI.

The following formatters were chosen:

- [goimports](https://pkg.go.dev/golang.org/x/tools/cmd/goimports) for `go`
- [shfmt](https://github.com/mvdan/sh) for `sh`, `bash`, `shell`
- [taplo](https://taplo.tamasfe.dev/cli/introduction.html) for `toml`

### Caveats and Workarounds

Some Go code snippets can't be formatted with `goimports`. I marked them with `golang` - this way, GitHub displays them as Go code, but `mdsf` will skip them. The similar for `shellsession` - `shfmt` can't format it, so I disabled.

Updates #1350

ccoVeille: You can also add the mdsf GHA action that @housegen added recently to mdsf
alexandear: > You can also add the mdsf GHA action that @housegen added recently to mdsf

Added using the manual from https://mdsf.mhouge.dk/#github-action
ccoVeille: > > You can also add the mdsf GHA action that @housegen added recently to mdsf
> 
> Added using the manual from https://mdsf.mhouge.dk/#github-action

Indeed, documentation is outdated

I opened

- https://github.com/hougesen/mdsf/issues/981
ccoVeille: > > > You can also add the mdsf GHA action that @housegen added recently to mdsf
> > 
> > 
> > Added using the manual from https://mdsf.mhouge.dk/#github-action
> 
> Indeed, documentation is outdated
> 
> I opened
> 
>     * [Update documentation about GHA hougesen/mdsf#981](https://github.com/hougesen/mdsf/issues/981)

I'm tired … and sorry.

Everything is fine. I through I had read the mdsf was installed via cargo 🤦‍♂️
ccoVeille: Is there anything blocking you @alexandear ?
alexandear: > Is there anything blocking you @alexandear ?

Yes. I'm thinking about formatting .sh and .toml files and need to find a suitable formatter.
ccoVeille: Most of them are listed on mdsf pages

sh: shfmt
toml: taplo
ccoVeille: I found [tombi](https://github.com/tombi-toml/tombi) to format TOML files.

And asked @hougesen to add it to [mdsf](https://github.com/hougesen/mdsf), and he did (thanks again)

- https://github.com/hougesen/mdsf/issues/1050

So now you can use taplo or tombi for the TOML code blocks

Patch:
diff --git a/.github/workflows/lint.yaml b/.github/workflows/lint.yaml
index c1269fb..6c3521b 100644
--- a/.github/workflows/lint.yaml
+++ b/.github/workflows/lint.yaml
@@ -61,6 +61,38 @@ jobs:
     runs-on: ubuntu-latest
     steps:
       - uses: actions/checkout@v4
-      - uses: DavidAnson/markdownlint-cli2-action@v20
+
+      - name: Setup Go
+        uses: actions/setup-go@v5
+        with:
+          go-version: stable
+
+      - name: Setup mdsf
+        uses: hougesen/mdsf@v0.9.5
+
+      - name: Setup goimports
+        # https://pkg.go.dev/golang.org/x/tools/cmd/goimports
+        run: go install golang.org/x/tools/cmd/goimports@v0.33.0
+
+      - name: Setup shfmt
+        # https://github.com/mvdan/sh#shfmt
+        run: go install mvdan.cc/sh/v3/cmd/shfmt@v3.11.0
+
+      - name: Setup taplo
+        # https://taplo.tamasfe.dev/cli/installation/binary.html
+        run: curl -fsSL https://github.com/tamasfe/taplo/releases/download/0.10.0/taplo-linux-x86_64.gz | gzip -d - | install -m 755 /dev/stdin /usr/local/bin/taplo
+
+      - name: Verify files format using markdownlint-cli2
+        uses: DavidAnson/markdownlint-cli2-action@v20
         with:
           config: .markdownlint-cli2.yaml
+
+      - name: Verify code snippets using mdsf
+        id: verify_snippets
+        run: mdsf verify --on-missing-language-definition ignore --on-missing-tool-binary fail-fast .
+
+      - name: Show diff when mdsf failed
+        if: failure() && steps.verify_snippets.outcome == 'failure'
+        run: |
+          mdsf format --debug --on-missing-language-definition ignore --on-missing-tool-binary fail-fast .
+          git diff --exit-code
diff --git a/DEVELOPING.md b/DEVELOPING.md
index 360d2da..6f6580e 100644
--- a/DEVELOPING.md
+++ b/DEVELOPING.md
@@ -61,7 +61,7 @@ We can set the banned identifier by using the TOML configuration file:
 
 ```toml
 [rule.ban-struct-name]
-  arguments = ["Foo"]
+arguments = ["Foo"]
 ```
 
 With the snippet above we:
@@ -89,12 +89,17 @@ type Formatter interface {
 
 ### Lint Markdown files
 
-We are using [markdownlint](https://github.com/DavidAnson/markdownlint) for checking Markdown files.
+We use [markdownlint](https://github.com/DavidAnson/markdownlint) and [mdsf](https://github.com/hougesen/mdsf) to check Markdown files.
+`markdownlint` verifies document formatting, such as line length and empty lines, while `mdsf` is responsible for formatting code snippets.
 
 1. Install [markdownlint-cli2](https://github.com/DavidAnson/markdownlint-cli2#install).
-2. Run the following command:
+2. Install [mdsf](https://mdsf.mhouge.dk/#installation) and formatters:
+    - [goimports](https://pkg.go.dev/golang.org/x/tools/cmd/goimports) for `go`: `go install golang.org/x/tools/cmd/goimports@latest`
+    - [shfmt](https://github.com/mvdan/sh#shfmt) for `sh, shell, bash`: `go install mvdan.cc/sh/v3/cmd/shfmt@latest`
+    - [taplo](https://taplo.tamasfe.dev/cli/installation/binary.html) for `toml`
+3. Run the following command to check formatting:
 
-```sh
+```shellsession
 $ markdownlint-cli2 .
 Finding: *.{md,markdown} *.md
 Found:
@@ -107,4 +112,14 @@ Linting: 5 file(s)
 Summary: 0 error(s)
 ```
 
-The tool automatically uses the config file [.markdownlint-cli2.yaml](./.markdownlint-cli2.yaml).
+_The `markdownlint-cli2` tool automatically uses the config file [.markdownlint-cli2.yaml](./.markdownlint-cli2.yaml)._
+\
+4. Run the following commands to verify and format code snippets:
+
+```sh
+mdsf verify .
+```
+
+```sh
+mdsf format .
+```
diff --git a/README.md b/README.md
index b55119b..3490c36 100644
--- a/README.md
+++ b/README.md
@@ -106,7 +106,7 @@ 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/)
 
 ```bash
-docker run -v "$(pwd)":/var/<repository> ghcr.io/mgechev/revive:v1.3.7 -config /var/<repository>/revive.toml -formatter stylish ./var/kidle/...
+docker run -v "$(pwd)":/var/YOUR_REPOSITORY ghcr.io/mgechev/revive:v1.3.7 -config /var/YOUR_REPOSITORY/revive.toml -formatter stylish ./var/YOUR_REPOSITORY/...
 ```
 
 - `-v` is for the volume
@@ -238,6 +238,7 @@ Using comments, you can disable the linter for the entire file or only a range o
 //revive:disable
 
 func Public() {}
+
 //revive:enable
 ```
 
@@ -251,8 +252,9 @@ You can do the same on a rule level. In case you want to disable only a particul
 ```go
 //revive:disable:unexported-return
 func Public() private {
-  return private
+	return private
 }
+
 //revive:enable:unexported-return
 ```
 
@@ -278,7 +280,7 @@ in the configuration. You can set the severity (defaults to _warning_) of the vi
 
 ```toml
 [directive.specify-disable-reason]
-    severity = "error"
+severity = "error"
 ```
 
 ### Configuration
@@ -305,11 +307,11 @@ warningCode = 0
 # Configuration of the `cyclomatic` rule. Here we specify that
 # the rule should fail if it detects code with higher complexity than 10.
 [rule.cyclomatic]
-  arguments = [10]
+arguments = [10]
 
 # Sets the severity of the `package-comments` rule to "error".
 [rule.package-comments]
-  severity = "error"
+severity = "error"
 ```
 
 By default `revive` will enable only the linting rules that are named in the configuration file.
@@ -328,7 +330,7 @@ For example:
 
 ```toml
 [rule.line-length-limit]
-    Disabled = true
+Disabled = true
 ```
 
 When enabling all rules you still need/can provide specific configurations for rules.
@@ -346,29 +348,29 @@ enableAllRules = true
 
 # Disabled rules
 [rule.blank-imports]
-    Disabled = true
+Disabled = true
 [rule.file-header]
-    Disabled = true
+Disabled = true
 [rule.max-public-structs]
-    Disabled = true
+Disabled = true
 [rule.line-length-limit]
-    Disabled = true
+Disabled = true
 [rule.function-length]
-    Disabled = true
+Disabled = true
 [rule.banned-characters]
-    Disabled = true
+Disabled = true
 
 # Rule tuning
 [rule.argument-limit]
-    Arguments = [5]
+Arguments = [5]
 [rule.cyclomatic]
-    Arguments = [10]
+Arguments = [10]
 [rule.cognitive-complexity]
-    Arguments = [7]
+Arguments = [7]
 [rule.function-result-limit]
-    Arguments = [3]
+Arguments = [3]
 [rule.error-strings]
-    Arguments = ["mypackage.Error"]
+Arguments = ["mypackage.Error"]
 ```
 
 ### Default Configuration
@@ -440,9 +442,9 @@ errorCode = 0
 warningCode = 0
 
 [rule.blank-imports]
-   Exclude=["**/*.pb.go"]
+Exclude = ["**/*.pb.go"]
 [rule.context-as-argument]
-   Exclude=["src/somepkg/*.go", "TEST"]
+Exclude = ["src/somepkg/*.go", "TEST"]
 ```
 
 You can use the following exclude patterns
@@ -560,7 +562,7 @@ in `golint` but optionally, you can relax it (see [golint/lint/issues/89](https:
 
 ```toml
 [rule.var-naming]
-  arguments = [["ID"], ["VM"]]
+arguments = [["ID"], ["VM"]]
 ```
 
 This way, revive will not warn for an identifier called `customId` but will warn that `customVm` should be called `customVM`.
@@ -657,7 +659,9 @@ func (f myRule) Name() string {
 	return "myRule"
 }
 
-func (f myRule) Apply(*lint.File, lint.Arguments) []lint.Failure { ... }
+func (f myRule) Apply(*lint.File, lint.Arguments) []lint.Failure {
+	// ...
+}
 ```
 
 You can still go further and use `revive` without its CLI, as part of your library, or your CLI:
@@ -666,39 +670,39 @@ You can still go further and use `revive` without its CLI, as part of your libra
 package mylib
 
 import (
-	"github.com/mgechev/revive/cli"
-	"github.com/mgechev/revive/revivelib"
+	"github.com/mgechev/revive/config"
 	"github.com/mgechev/revive/lint"
+	"github.com/mgechev/revive/revivelib"
 )
 
 // Error checking removed for clarity
 func LintMyFile(file string) {
-	conf, _:= config.GetConfig("../defaults.toml")
+	conf, _ := config.GetConfig("../defaults.toml")
 
 	revive, _ := revivelib.New(
-		conf,  // Configuration file
-		true,  // Set exit status
-		2048,  // Max open files
+		conf, // Configuration file
+		true, // Set exit status
+		2048, // Max open files
 
 		// Then add as many extra rules as you need
 		revivelib.NewExtraRule(&myRule{}, lint.RuleConfig{}),
 	)
 
 	failuresChan, err := revive.Lint(
- 		revivelib.Include(file),
- 		revivelib.Exclude("./fixtures"),
- 		// You can use as many revivelib.Include or revivelib.Exclude as required
- 	)
-  	if err != nil {
-  	 	panic("Shouldn't have failed: " + err.Error())
-  	}
-
-  	// Now let's return the formatted errors
+		revivelib.Include(file),
+		revivelib.Exclude("./fixtures"),
+		// You can use as many revivelib.Include or revivelib.Exclude as required
+	)
+	if err != nil {
+		panic("Shouldn't have failed: " + err.Error())
+	}
+
+	// Now let's return the formatted errors
 	failures, exitCode, _ := revive.Format("stylish", failuresChan)
 
-  	// failures is the string with all formatted lint error messages
-  	// exit code is 0 if no errors, 1 if errors (unless config options change it)
-  	// ... do something with them
+	// failures is the string with all formatted lint error messages
+	// exit code is 0 if no errors, 1 if errors (unless config options change it)
+	// ... do something with them
 }
 
 type myRule struct{}
@@ -707,7 +711,9 @@ func (f myRule) Name() string {
 	return "myRule"
 }
 
-func (f myRule) Apply(*lint.File, lint.Arguments) []lint.Failure { ... }
+func (f myRule) Apply(*lint.File, lint.Arguments) []lint.Failure {
+	// ...
+}
 ```
 
 ### Custom Formatter
@@ -734,8 +740,8 @@ Here's a basic performance benchmark on MacBook Pro Early 2013 run on Kubernetes
 
 ### golint
 
-```shell
-time golint kubernetes/... > /dev/null
+```shellsession
+$ time golint kubernetes/... > /dev/null
 
 real    0m54.837s
 user    0m57.844s
@@ -744,9 +750,9 @@ sys     0m9.146s
 
 ### revive's speed
 
-```shell
+```shellsession
 # no type checking
-time revive -config untyped.toml kubernetes/... > /dev/null
+$ time revive -config untyped.toml kubernetes/... > /dev/null
 
 real    0m8.471s
 user    0m40.721s
@@ -755,9 +761,9 @@ sys     0m3.262s
 
 Keep in mind that if you use rules that require type checking, the performance may drop to 2x faster than `golint`:
 
-```shell
+```shellsession
 # type checking enabled
-time revive kubernetes/... > /dev/null
+$ time revive kubernetes/... > /dev/null
 
 real    0m26.211s
 user    2m6.708s
diff --git a/RULES_DESCRIPTIONS.md b/RULES_DESCRIPTIONS.md
index 183b080..c4c39d9 100644
--- a/RULES_DESCRIPTIONS.md
+++ b/RULES_DESCRIPTIONS.md
@@ -106,12 +106,16 @@ Examples:
 
 ```toml
 [rule.add-constant]
-  arguments = [{ maxLitCount = "3", allowStrs = "\"\"", allowInts = "0,1,2", allowFloats = "0.0,0.,1.0,1.,2.0,2.", ignoreFuncs = "os\\.*,fmt\\.Println,make" }]
+arguments = [
+  { maxLitCount = "3", allowStrs = "\"\"", allowInts = "0,1,2", allowFloats = "0.0,0.,1.0,1.,2.0,2.", ignoreFuncs = "os\\.*,fmt\\.Println,make" },
+]
 ```
 
 ```toml
 [rule.add-constant]
-  arguments = [{ max-lit-count = "3", allow-strs = "\"\"", allow-ints = "0,1,2", allow-floats = "0.0,0.,1.0,1.,2.0,2.", ignore-funcs = "os\\.*,fmt\\.Println,make" }]
+arguments = [
+  { max-lit-count = "3", allow-strs = "\"\"", allow-ints = "0,1,2", allow-floats = "0.0,0.,1.0,1.,2.0,2.", ignore-funcs = "os\\.*,fmt\\.Println,make" },
+]
 ```
 
 ## argument-limit
@@ -125,7 +129,7 @@ Example:
 
 ```toml
 [rule.argument-limit]
-  arguments = [4]
+arguments = [4]
 ```
 
 ## atomic
@@ -144,7 +148,7 @@ Example:
 
 ```toml
 [rule.banned-characters]
-  arguments = ["Ω","Σ","σ"]
+arguments = ["Ω", "Σ", "σ"]
 ```
 
 ## bare-return
@@ -187,7 +191,7 @@ Example:
 
 ```toml
 [rule.cognitive-complexity]
-  arguments = [7]
+arguments = [7]
 ```
 
 ## comment-spacings
@@ -211,7 +215,7 @@ Example:
 
 ```toml
 [rule.comment-spacings]
-  arguments = ["mypragma:", "+optional"]
+arguments = ["mypragma:", "+optional"]
 ```
 
 ## comments-density
@@ -225,7 +229,7 @@ Example:
 
 ```toml
 [rule.comments-density]
-  arguments = [15]
+arguments = [15]
 ```
 
 ## confusing-naming
@@ -259,12 +263,16 @@ Examples:
 
 ```toml
 [rule.context-as-argument]
-  arguments = [{allowTypesBefore = "*testing.T,*github.com/user/repo/testing.Harness"}]
+arguments = [
+  { allowTypesBefore = "*testing.T,*github.com/user/repo/testing.Harness" },
+]
 ```
 
 ```toml
 [rule.context-as-argument]
-  arguments = [{allow-types-before = "*testing.T,*github.com/user/repo/testing.Harness"}]
+arguments = [
+  { allow-types-before = "*testing.T,*github.com/user/repo/testing.Harness" },
+]
 ```
 
 ## context-keys-type
@@ -284,7 +292,7 @@ Example:
 
 ```toml
 [rule.cyclomatic]
-  arguments = [3]
+arguments = [3]
 ```
 
 ## datarace
@@ -328,12 +336,12 @@ Examples:
 
 ```toml
 [rule.defer]
-  arguments = [["callChain", "loop"]]
+arguments = [["callChain", "loop"]]
 ```
 
 ```toml
 [rule.defer]
-  arguments = [["call-chain", "loop"]]
+arguments = [["call-chain", "loop"]]
 ```
 
 ## dot-imports
@@ -351,12 +359,22 @@ Examples:
 
 ```toml
 [rule.dot-imports]
-  arguments = [{ allowedPackages = ["github.com/onsi/ginkgo/v2","github.com/onsi/gomega"] }]
+arguments = [
+  { allowedPackages = [
+    "github.com/onsi/ginkgo/v2",
+    "github.com/onsi/gomega",
+  ] },
+]
 ```
 
 ```toml
 [rule.dot-imports]
-  arguments = [{ allowed-packages = ["github.com/onsi/ginkgo/v2","github.com/onsi/gomega"] }]
+arguments = [
+  { allowed-packages = [
+    "github.com/onsi/ginkgo/v2",
+    "github.com/onsi/gomega",
+  ] },
+]
 ```
 
 ## duplicated-imports
@@ -370,21 +388,21 @@ _Configuration_: N/A
 _Description_: In Go it is idiomatic to minimize nesting statements, a typical example is to avoid if-then-else constructions.
 This rule spots constructions like
 
-```go
+```golang
 if cond {
-  // do something
+	// do something
 } else {
-  // do other thing
-  return ...
+	// do other thing
+	return ...
 }
 ```
 
 where the `if` condition may be inverted in order to reduce nesting:
 
-```go
+```golang
 if !cond {
-  // do other thing
-  return ...
+	// do other thing
+	return ...
 }
 
 // do something
@@ -400,12 +418,12 @@ Examples:
 
 ```toml
 [rule.early-return]
-  arguments = ["preserveScope", "allowJump"]
+arguments = ["preserveScope", "allowJump"]
 ```
 
 ```toml
 [rule.early-return]
-  arguments = ["preserve-scope", "allow-jump"]
+arguments = ["preserve-scope", "allow-jump"]
 ```
 
 ## empty-block
@@ -436,7 +454,7 @@ Example:
 
 ```toml
 [rule.enforce-map-style]
-  arguments = ["make"]
+arguments = ["make"]
 ```
 
 ## enforce-repeated-arg-type-style
@@ -465,19 +483,19 @@ Example (1):
 
 ```toml
 [rule.enforce-repeated-arg-type-style]
-  arguments = ["short"]
+arguments = ["short"]
 ```
 
 Examples (2):
 
 ```toml
 [rule.enforce-repeated-arg-type-style]
-  arguments = [{ funcArgStyle = "full", funcRetValStyle = "short" }]
+arguments = [{ funcArgStyle = "full", funcRetValStyle = "short" }]
 ```
 
 ```toml
 [rule.enforce-repeated-arg-type-style]
-  arguments = [{ func-arg-style = "full", func-ret-val-style = "short" }]
+arguments = [{ func-arg-style = "full", func-ret-val-style = "short" }]
 ```
 
 ## enforce-slice-style
@@ -497,7 +515,7 @@ Example:
 
 ```toml
 [rule.enforce-slice-style]
-  arguments = ["make"]
+arguments = ["make"]
 ```
 
 ## error-naming
@@ -527,7 +545,7 @@ Example:
 
 ```toml
 [rule.error-strings]
-  arguments = ["xerrors.Errorf"]
+arguments = ["xerrors.Errorf"]
 ```
 
 ## errorf
@@ -563,12 +581,22 @@ Examples:
 
 ```toml
 [rule.exported]
-  arguments = ["checkPrivateReceivers", "disableStutteringCheck", "checkPublicInterface", "disableChecksOnFunctions"]
+arguments = [
+  "checkPrivateReceivers",
+  "disableStutteringCheck",
+  "checkPublicInterface",
+  "disableChecksOnFunctions",
+]
 ```
 
 ```toml
 [rule.exported]
-  arguments = ["check-private-receivers", "disable-stuttering-check", "check-public-interface", "disable-checks-on-functions"]
+arguments = [
+  "check-private-receivers",
+  "disable-stuttering-check",
+  "check-public-interface",
+  "disable-checks-on-functions",
+]
 ```
 
 ## file-header
@@ -581,7 +609,7 @@ Example:
 
 ```toml
 [rule.file-header]
-  arguments = ["This is the text that must appear at the top of source files."]
+arguments = ["This is the text that must appear at the top of source files."]
 ```
 
 ## file-length-limit
@@ -598,12 +626,12 @@ Examples:
 
 ```toml
 [rule.file-length-limit]
-  arguments = [{max=100,skipComments=true,skipBlankLines=true}]
+arguments = [{ max = 100, skipComments = true, skipBlankLines = true }]
 ```
 
 ```toml
 [rule.file-length-limit]
-  arguments = [{max=100,skip-comments=true,skip-blank-lines=true}]
+arguments = [{ max = 100, skip-comments = true, skip-blank-lines = true }]
 ```
 
 ## filename-format
@@ -617,7 +645,7 @@ Example:
 
 ```toml
 [rule.filename-format]
-  arguments=["^[_a-z][_a-z0-9]*\\.go$"]
+arguments = ["^[_a-z][_a-z0-9]*\\.go$"]
 ```
 
 ## flag-parameter
@@ -638,7 +666,7 @@ Example:
 
 ```toml
 [rule.function-length]
-  arguments = [10, 0]
+arguments = [10, 0]
 ```
 
 Will check for functions exceeding 10 statements and will not check the number of lines of functions
@@ -653,7 +681,7 @@ Example:
 
 ```toml
 [rule.function-result-limit]
-  arguments = [3]
+arguments = [3]
 ```
 
 ## get-return
@@ -696,19 +724,19 @@ Example (1):
 
 ```toml
 [rule.import-alias-naming]
-  arguments = ["^[a-z][a-z0-9]{0,}$"]
+arguments = ["^[a-z][a-z0-9]{0,}$"]
 ```
 
 Examples (2):
 
 ```toml
 [rule.import-alias-naming]
-  arguments = [{ allowRegex = "^[a-z][a-z0-9]{0,}$", denyRegex = '^v\d+$' }]
+arguments = [{ allowRegex = "^[a-z][a-z0-9]{0,}$", denyRegex = '^v\d+$' }]
 ```
 
 ```toml
 [rule.import-alias-naming]
-  arguments = [{ allow-regex = "^[a-z][a-z0-9]{0,}$", deny-regex = '^v\d+$' }]
+arguments = [{ allow-regex = "^[a-z][a-z0-9]{0,}$", deny-regex = '^v\d+$' }]
 ```
 
 ## import-shadowing
@@ -729,7 +757,7 @@ Example:
 
 ```toml
 [rule.imports-blocklist]
-  arguments = ["crypto/md5", "crypto/sha1", "crypto/**/pkix"]
+arguments = ["crypto/md5", "crypto/sha1", "crypto/**/pkix"]
 ```
 
 ## increment-decrement
@@ -754,12 +782,12 @@ Examples:
 
 ```toml
 [rule.indent-error-flow]
-  arguments = ["preserveScope"]
+arguments = ["preserveScope"]
 ```
 
 ```toml
 [rule.indent-error-flow]
-  arguments = ["preserve-scope"]
+arguments = ["preserve-scope"]
 ```
 
 ## line-length-limit
@@ -772,7 +800,7 @@ Example:
 
 ```toml
 [rule.line-length-limit]
-  arguments = [80]
+arguments = [80]
 ```
 
 ## max-control-nesting
@@ -785,7 +813,7 @@ Example:
 
 ```toml
 [rule.max-control-nesting]
-  arguments = [3]
+arguments = [3]
 ```
 
 ## max-public-structs
@@ -801,7 +829,7 @@ Example:
 
 ```toml
 [rule.max-public-structs]
-  arguments = [3]
+arguments = [3]
 ```
 
 ## modifies-parameter
@@ -838,13 +866,13 @@ _Configuration_: N/A
 
 Example:
 
-```go
+```golang
 if isGenerated(content) && !config.IgnoreGeneratedHeader {
 ```
 
 Swap left and right side :
 
-```go
+```golang
 if !config.IgnoreGeneratedHeader && isGenerated(content) {
 ```
 
@@ -895,12 +923,12 @@ Examples:
 
 ```toml
 [rule.receiver-naming]
-  arguments = [{maxLength=2}]
+arguments = [{ maxLength = 2 }]
 ```
 
 ```toml
 [rule.receiver-naming]
-  arguments = [{max-length=2}]
+arguments = [{ max-length = 2 }]
 ```
 
 ## redefines-builtin-id
@@ -962,12 +990,28 @@ Example:
 
 ```toml
 [rule.string-format]
-  arguments = [
-    ["core.WriteError[1].Message", "/^([^A-Z]|$)/", "must not start with a capital letter"],
-    ["fmt.Errorf[0]", "/(^|[^\\.!?])$/", "must not end in punctuation"],
-    ["panic", "/^[^\\n]*$/", "must not contain line breaks"],
-    ["fmt.Errorf[0],core.WriteError[1].Message", "!/^.*%w.*$/", "must not contain '%w'"],
-  ]
+arguments = [
+  [
+    "core.WriteError[1].Message",
+    "/^([^A-Z]|$)/",
+    "must not start with a capital letter",
+  ],
+  [
+    "fmt.Errorf[0]",
+    "/(^|[^\\.!?])$/",
+    "must not end in punctuation",
+  ],
+  [
+    "panic",
+    "/^[^\\n]*$/",
+    "must not contain line breaks",
+  ],
+  [
+    "fmt.Errorf[0],core.WriteError[1].Message",
+    "!/^.*%w.*$/",
+    "must not contain '%w'",
+  ],
+]
 ```
 
 ## string-of-int
@@ -990,7 +1034,7 @@ To accept the `inline` option in JSON tags (and `outline` and `gnu` in BSON tags
 
 ```toml
 [rule.struct-tag]
-  arguments = ["json,inline", "bson,outline,gnu"]
+arguments = ["json,inline", "bson,outline,gnu"]
 ```
 
 ## superfluous-else
@@ -1006,12 +1050,12 @@ Examples:
 
 ```toml
 [rule.superfluous-else]
-  arguments = ["preserveScope"]
+arguments = ["preserveScope"]
 ```
 
 ```toml
 [rule.superfluous-else]
-  arguments = ["preserve-scope"]
+arguments = ["preserve-scope"]
 ```
 
 ## time-date
@@ -1025,11 +1069,13 @@ _Example_:
 Here the leading zeros are defining integers with octal notation
 
 ```go
+import "time"
+
 var (
-  // here we can imagine zeroes were used for padding purpose
-  a = time.Date(2023, 1, 2, 3, 4, 0, 00000000, time.UTC) // 00000000 is octal and equals 0 in decimal
-  b = time.Date(2023, 1, 2, 3, 4, 0, 00000006, time.UTC) // 00000006 is octal and equals 6 in decimal
-  c = time.Date(2023, 1, 2, 3, 4, 0, 00123456, time.UTC) // 00123456 is octal and equals 42798 in decimal
+	// here we can imagine zeroes were used for padding purpose
+	a = time.Date(2023, 1, 2, 3, 4, 0, 00000000, time.UTC) // 00000000 is octal and equals 0 in decimal
+	b = time.Date(2023, 1, 2, 3, 4, 0, 00000006, time.UTC) // 00000006 is octal and equals 6 in decimal
+	c = time.Date(2023, 1, 2, 3, 4, 0, 00123456, time.UTC) // 00123456 is octal and equals 42798 in decimal
 )
 ```
 
@@ -1044,15 +1090,17 @@ This rule also reports strange notations used with time.Date.
 Example:
 
 ```go
+import "time"
+
 var _ = time.Date(
-		0x7e7,    // hexadecimal notation: use 2023 instead of 0x7e7/
-		0b1,      // binary notation: use 1 instead of 0b1/
-		0x_2,     // hexadecimal notation: use 2 instead of 0x_2/
-		1_3,      // alternative notation: use 13 instead of 1_3/
-		1e1,      // exponential notation: use 10 instead of 1e1/
-		0.,       // float literal: use 0 instead of 0./
-		0x1.Fp+6, // float literal: use 124 instead of 0x1.Fp+6/
-		time.UTC)
+	0x7e7,    // hexadecimal notation: use 2023 instead of 0x7e7/
+	0b1,      // binary notation: use 1 instead of 0b1/
+	0x_2,     // hexadecimal notation: use 2 instead of 0x_2/
+	1_3,      // alternative notation: use 13 instead of 1_3/
+	1e1,      // exponential notation: use 10 instead of 1e1/
+	0.,       // float literal: use 0 instead of 0./
+	0x1.Fp+6, // float literal: use 124 instead of 0x1.Fp+6/
+	time.UTC)
 ```
 
 All these are considered to be an uncommon usage of time.Date, are reported with a 0.8 confidence.
@@ -1060,6 +1108,8 @@ All these are considered to be an uncommon usage of time.Date, are reported with
 Note: even if 00, 01, 02, 03, 04, 05, 06, 07 are octal numbers, they can be considered as valid, and reported with 0.5 confidence.
 
 ```go
+import "time"
+
 var _ = time.Date(2023, 01, 02, 03, 04, 00, 0, time.UTC)
 ```
 
@@ -1086,7 +1136,7 @@ _Configuration_: list of key-value-pair-map (`[]map[string]any`).
 - `acceptIgnoredAssertionResult` (`acceptignoredassertionresult`, `accept-ignored-assertion-result`): (bool) default `false`,
 set it to `true` to accept ignored type assertion results like this:
 
-```go
+```golang
 foo, _ := bar(.*Baz).
 //   ^
 ```
@@ -1095,12 +1145,12 @@ Examples:
 
 ```toml
 [rule.unchecked-type-assertion]
-  arguments = [{acceptIgnoredAssertionResult=true}]
+arguments = [{ acceptIgnoredAssertionResult = true }]
 ```
 
 ```toml
 [rule.unchecked-type-assertion]
-  arguments = [{accept-ignored-assertion-result=true}]
+arguments = [{ accept-ignored-assertion-result = true }]
 ```
 
 ## unconditional-recursion
@@ -1132,7 +1182,13 @@ Example:
 
 ```toml
 [rule.unhandled-error]
-  arguments = ["os\.(Create|WriteFile|Chmod)", "fmt\.Print", "myFunction", "net\..*", "bytes\.Buffer\.Write"]
+arguments = [
+  'os\.(Create|WriteFile|Chmod)',
+  'fmt\.Print',
+  'myFunction',
+  'net\..*',
+  'bytes\.Buffer\.Write',
+]
 ```
 
 ## unnecessary-stmt
@@ -1164,12 +1220,12 @@ func SomeFunc(_someObj *MyStruct) {} // matches rule
 
 ```toml
 [rule.unused-parameter]
-  arguments = [{ allowRegex = "^_" }]
+arguments = [{ allowRegex = "^_" }]
 ```
 
 ```toml
 [rule.unused-parameter]
-  arguments = [{ allow-regex = "^_" }]
+arguments = [{ allow-regex = "^_" }]
 ```
 
 ## unused-receiver
@@ -1189,12 +1245,12 @@ func (_my *MyStruct) SomeMethod() {} // matches rule
 
 ```toml
 [rule.unused-receiver]
-  arguments = [{ allowRegex = "^_" }]
+arguments = [{ allowRegex = "^_" }]
 ```
 
 ```toml
 [rule.unused-receiver]
-  arguments = [{ allow-regex = "^_" }]
+arguments = [{ allow-regex = "^_" }]
 ```
 
 ## use-any
@@ -1248,32 +1304,32 @@ Examples:
 
 ```toml
 [rule.var-naming]
-  arguments = [["ID"], ["VM"], [{upperCaseConst=true}]]
+arguments = [["ID"], ["VM"], [{ upperCaseConst = true }]]
 ```
 
 ```toml
 [rule.var-naming]
-  arguments = [[], [], [{skipPackageNameChecks=true}]]
+arguments = [[], [], [{ skipPackageNameChecks = true }]]
 ```
 
 ```toml
 [rule.var-naming]
-  arguments = [[], [], [{extraBadPackageNames=["helpers", "models"]}]]
+arguments = [[], [], [{ extraBadPackageNames = ["helpers", "models"] }]]
 ```
 
 ```toml
 [rule.var-naming]
-  arguments = [["ID"], ["VM"], [{upper-case-const=true}]]
+arguments = [["ID"], ["VM"], [{ upper-case-const = true }]]
 ```
 
 ```toml
 [rule.var-naming]
-  arguments = [[], [], [{skip-package-name-checks=true}]]
+arguments = [[], [], [{ skip-package-name-checks = true }]]
 ```
 
 ```toml
 [rule.var-naming]
-  arguments = [[], [], [{extra-bad-package-names=["helpers", "models"]}]]
+arguments = [[], [], [{ extra-bad-package-names = ["helpers", "models"] }]]
 ```
 
 ## waitgroup-by-value
diff --git a/cli/main.go b/cli/main.go
index 07e0024..c703643 100644
--- a/cli/main.go
+++ b/cli/main.go
@@ -101,24 +101,24 @@ var (
 
 var originalUsage = flag.Usage
 
-func getLogo() string {
+func logo() string {
 	return color.YellowString(` _ __ _____   _(_)__  _____
 | '__/ _ \ \ / / \ \ / / _ \
 | | |  __/\ V /| |\ V /  __/
 |_|  \___| \_/ |_| \_/ \___|`)
 }
 
-func getCall() string {
+func call() string {
 	return color.MagentaString("revive -config c.toml -formatter friendly -exclude a.go -exclude b.go ./...")
 }
 
-func getBanner() string {
+func banner() string {
 	return fmt.Sprintf(`
 %s
 
 Example:
   %s
-`, getLogo(), getCall())
+`, logo(), call())
 }
 
 func buildDefaultConfigPath() string {
@@ -150,7 +150,7 @@ func initConfig() {
 	}
 
 	flag.Usage = func() {
-		fmt.Println(getBanner())
+		fmt.Println(banner())
 		originalUsage()
 	}
 
diff --git a/formatter/checkstyle.go b/formatter/checkstyle.go
index 8fe85fa..9b91d8e 100644
--- a/formatter/checkstyle.go
+++ b/formatter/checkstyle.go
@@ -43,7 +43,7 @@ func (*Checkstyle) Format(failures <-chan lint.Failure, config lint.Config) (str
 			Severity:   severity(config, failure),
 			RuleName:   failure.RuleName,
 		}
-		fn := failure.GetFilename()
+		fn := failure.Filename()
 		if issues[fn] == nil {
 			issues[fn] = []issue{}
 		}
diff --git a/formatter/friendly.go b/formatter/friendly.go
index 570589d..6a16d6f 100644
--- a/formatter/friendly.go
+++ b/formatter/friendly.go
@@ -68,7 +68,7 @@ func (*Friendly) printHeaderRow(sb *strings.Builder, failure lint.Failure, sever
 }
 
 func (*Friendly) printFilePosition(sb *strings.Builder, failure lint.Failure) {
-	fmt.Fprintf(sb, "  %s:%d:%d", failure.GetFilename(), failure.Position.Start.Line, failure.Position.Start.Column)
+	fmt.Fprintf(sb, "  %s:%d:%d", failure.Filename(), failure.Position.Start.Line, failure.Position.Start.Column)
 }
 
 type statEntry struct {
diff --git a/formatter/stylish.go b/formatter/stylish.go
index b812309..f44ada8 100644
--- a/formatter/stylish.go
+++ b/formatter/stylish.go
@@ -27,7 +27,7 @@ func formatFailure(failure lint.Failure, severity lint.Severity) []string {
 	if severity == lint.SeverityWarning {
 		fName = color.YellowString(fURL)
 	}
-	return []string{failure.GetFilename(), pos, fName, fString}
+	return []string{failure.Filename(), pos, fName, fString}
 }
 
 // Format formats the failures gotten from the lint.
diff --git a/lint/failure.go b/lint/failure.go
index 48095f9..c9a6d72 100644
--- a/lint/failure.go
+++ b/lint/failure.go
@@ -81,7 +81,14 @@ type Failure struct {
 }
 
 // GetFilename returns the filename.
+//
+// Deprecated: Use [Filename].
 func (f *Failure) GetFilename() string {
+	return f.Filename()
+}
+
+// Filename returns the filename.
+func (f *Failure) Filename() string {
 	return f.Position.Start.Filename
 }
 
diff --git a/mdsf.json b/mdsf.json
new file mode 100644
index 0000000..4a1c819
--- /dev/null
+++ b/mdsf.json
@@ -0,0 +1,13 @@
+{
+  "$schema": "https://raw.githubusercontent.com/hougesen/mdsf/HEAD/schemas/v0.9.5/mdsf.schema.json",
+  "format_finished_document": false,
+  "languages": {
+    "go": "goimports",
+    "shell": "shfmt",
+    "toml": "taplo"
+  },
+  "language_aliases": {
+    "bash": "shell",
+    "sh": "shell"
+  }
+}
diff --git a/revivelib/core.go b/revivelib/core.go
index 86aff1a..0e237f4 100755
--- a/revivelib/core.go
+++ b/revivelib/core.go
@@ -75,9 +75,9 @@ func (r *Revive) Lint(patterns ...*LintPattern) (<-chan lint.Failure, error) {
 
 	for _, lintpkg := range patterns {
 		if lintpkg.IsExclude() {
-			excludePatterns = append(excludePatterns, lintpkg.GetPattern())
+			excludePatterns = append(excludePatterns, lintpkg.Pattern())
 		} else {
-			includePatterns = append(includePatterns, lintpkg.GetPattern())
+			includePatterns = append(includePatterns, lintpkg.Pattern())
 		}
 	}
 
diff --git a/revivelib/pattern.go b/revivelib/pattern.go
index fe87fc0..16d8d4d 100644
--- a/revivelib/pattern.go
+++ b/revivelib/pattern.go
@@ -12,7 +12,14 @@ func (p *LintPattern) IsExclude() bool {
 }
 
 // GetPattern - returns the actual pattern
+//
+// Deprecated: Use [Pattern].
 func (p *LintPattern) GetPattern() string {
+	return p.Pattern()
+}
+
+// Pattern - returns the actual pattern
+func (p *LintPattern) Pattern() string {
 	return p.pattern
 }
 
diff --git a/rule/package_comments.go b/rule/package_comments.go
index 20afee8..74af626 100644
--- a/rule/package_comments.go
+++ b/rule/package_comments.go
@@ -143,7 +143,7 @@ func (l *lintPackageComments) Visit(_ ast.Node) ast.Visitor {
 		}
 	}
 
-	if l.fileAst.Doc == nil {
+	if isEmptyDoc(l.fileAst.Doc) {
 		for _, failure := range l.checkPackageComment() {
 			l.onFailure(failure)
 		}
@@ -162,3 +162,7 @@ func (l *lintPackageComments) Visit(_ ast.Node) ast.Visitor {
 	}
 	return nil
 }
+
+func isEmptyDoc(commentGroup *ast.CommentGroup) bool {
+	return commentGroup == nil || commentGroup.Text() == ""
+}


Test patch:
diff --git a/test/golint_test.go b/test/golint_test.go
index 263b089..70dd577 100644
--- a/test/golint_test.go
+++ b/test/golint_test.go
@@ -63,12 +63,9 @@ func TestAll(t *testing.T) {
 				t.Fatalf("Failed reading %s: %v", fi.Name(), err)
 			}
 
-			fileInfo, err := os.Stat(filePath)
-			if err != nil {
-				t.Fatalf("Failed reading %s: %v", fi.Name(), err)
-			}
+			ins := parseInstructions(t, filePath, src)
 
-			if err := assertFailures(t, filepath.Dir(baseDir), fileInfo, src, rules, map[string]lint.RuleConfig{}); err != nil {
+			if err := assertFailures(t, filePath, rules, map[string]lint.RuleConfig{}, ins); err != nil {
 				t.Errorf("Linting %s: %v", fi.Name(), err)
 			}
 		})
diff --git a/test/utils_test.go b/test/utils_test.go
index 6ffc509..63b5b2c 100644
--- a/test/utils_test.go
+++ b/test/utils_test.go
@@ -42,10 +42,7 @@ func testRule(t *testing.T, filename string, rule lint.Rule, config ...*lint.Rul
 	if err != nil {
 		t.Fatalf("Bad filename path in test for %s: %v", rule.Name(), err)
 	}
-	stat, err := os.Stat(fullFilePath)
-	if err != nil {
-		t.Fatalf("Cannot get file info for %s: %v", rule.Name(), err)
-	}
+
 	var ruleConfig lint.RuleConfig
 	c := map[string]lint.RuleConfig{}
 	if len(config) > 0 {
@@ -54,19 +51,19 @@ func testRule(t *testing.T, filename string, rule lint.Rule, config ...*lint.Rul
 	}
 	configureRule(t, rule, ruleConfig.Arguments)
 
-	if parseInstructions(t, fullFilePath, src) == nil {
-		assertSuccess(t, baseDir, stat, []lint.Rule{rule}, c)
+	ins := parseInstructions(t, fullFilePath, src)
+	if ins == nil {
+		assertSuccess(t, fullFilePath, []lint.Rule{rule}, c)
 		return
 	}
-	assertFailures(t, baseDir, stat, src, []lint.Rule{rule}, c)
+	assertFailures(t, fullFilePath, []lint.Rule{rule}, c, ins)
 }
 
-func assertSuccess(t *testing.T, baseDir string, fi os.FileInfo, rules []lint.Rule, config map[string]lint.RuleConfig) error {
+func assertSuccess(t *testing.T, filePath string, rules []lint.Rule, config map[string]lint.RuleConfig) error {
 	t.Helper()
 
 	l := lint.New(os.ReadFile, 0)
 
-	filePath := filepath.Join(baseDir, fi.Name())
 	ps, err := l.Lint([][]string{{filePath}}, rules, lint.Config{
 		Rules: config,
 	})
@@ -84,14 +81,12 @@ func assertSuccess(t *testing.T, baseDir string, fi os.FileInfo, rules []lint.Ru
 	return nil
 }
 
-func assertFailures(t *testing.T, baseDir string, fi os.FileInfo, src []byte, rules []lint.Rule, config map[string]lint.RuleConfig) error {
+func assertFailures(t *testing.T, filePath string, rules []lint.Rule, config map[string]lint.RuleConfig, ins []instruction) error {
 	t.Helper()
 
 	l := lint.New(os.ReadFile, 0)
 
-	ins := parseInstructions(t, filepath.Join(baseDir, fi.Name()), src)
-
-	ps, err := l.Lint([][]string{{filepath.Join(baseDir, fi.Name())}}, rules, lint.Config{
+	ps, err := l.Lint([][]string{{filePath}}, rules, lint.Config{
 		Rules: config,
 	})
 	if err != nil {
@@ -119,13 +114,13 @@ func assertFailures(t *testing.T, baseDir string, fi os.FileInfo, src []byte, ru
 						r = r[:i]
 					}
 					if r != in.Replacement {
-						t.Errorf("Lint failed at %s:%d; got replacement %q, want %q", fi.Name(), in.Line, r, in.Replacement)
+						t.Errorf("Lint failed at %s:%d; got replacement %q, want %q", filePath, in.Line, r, in.Replacement)
 					}
 				}
 
 				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)
+						t.Errorf("Lint failed at %s:%d; got confidence %f, want %f", filePath, in.Line, p.Confidence, in.Confidence)
 					}
 				}
 
@@ -138,11 +133,11 @@ func assertFailures(t *testing.T, baseDir string, fi os.FileInfo, src []byte, ru
 			}
 		}
 		if !ok {
-			t.Errorf("Lint failed at %s:%d; /%v/ did not match", fi.Name(), in.Line, in.Match)
+			t.Errorf("Lint failed at %s:%d; /%v/ did not match", filePath, in.Line, in.Match)
 		}
 	}
 	for _, p := range failures {
-		t.Errorf("Unexpected problem at %s:%d: %v", fi.Name(), p.Position.Start.Line, p.Failure)
+		t.Errorf("Unexpected problem at %s:%d: %v", filePath, p.Position.Start.Line, p.Failure)
 	}
 	return nil
 }
diff --git a/testdata/golint/package_doc7.go b/testdata/golint/package_doc7.go
new file mode 100644
index 0000000..db191ee
--- /dev/null
+++ b/testdata/golint/package_doc7.go
@@ -0,0 +1,2 @@
+//nolint:dupl
+package foo