Repository: mgechev/revive
Pull request #1272 (2025-03-15 18:46:31)

rule: allow lowercased and kebab-cased options
This PR allows rule options to be specified in lowercase and kebab case. Previously, the `context-as-argument` rule accepts camelCased argument `allowTypesBefore`. Now, it also accepts kebab-cased `allow-types-before` and the fully lowercased `allowtypesbefore` option.

This PR addresses the following issues:

- Allows users to specify options in lowercase (see golangci/golangci-lint#3280).
- Ensures consistent casing for all revive rule options. For example, most rules use `camelCase` for options, but `context-keys-type` uses `kebab-cased` options like `call-chain`, `method-call`, and `immediate-recover`.
- Aligns revive rule options with those of other Go linters, which commonly use kebab-case or lowercase options. See [.golangci.reference.yml](https://github.com/golangci/golangci-lint/blob/7bcf51e684a1fae1ff72b6f74f6451bc11d67ce7/.golangci.reference.yml#L1955-L1982).

I've refactored `early-return`, `indent-error-flow`, and `superfluous-else` rules by adding the `Configure` method.

I’ve added tests to ensure this PR does not introduce regressions.

Also, updated documentation.

To simplify the review process, I have broken the changes into separate commits, each representing a distinct logical update.

ccoVeille: Also, one question,

I saw you worked on golangci-lint too

https://github.com/golangci/golangci-lint/pull/5562

 do you plan to update the exanples in json schema too golangci-lint reference.yaml ?

https://github.com/golangci/golangci-lint/blob/main/jsonschema%2Fgolangci.next.jsonschema.json#L2826-L2849

Also, how much work do you think it could be to add for each rule the list of parameter it supports.
alexandear: > My feedbacks could also be addressed in another PR maybe.

Thanks for the review! I’ve fixed half of the comments. The other comments are about making error messages consistent and should be addressed in a separate PR to avoid overcomplicating this one. In this PR, I tried to leave them unchanged.

> I saw you worked on golangci-lint too
> 
> * [docs: add arguments to a few revive rules golangci/golangci-lint#5562](https://github.com/golangci/golangci-lint/pull/5562)

The PR golangci/golangci-lint#5562 adds missing parameters to the golangci-lint documantation. We will create a PR to switch from `camelCase` to `kebab-case` after the new `revive` version is released with this change.

> do you plan to update the exanples in json schema too golangci-lint reference.yaml ?

No, I'm not. Ludovic usually updates the schema.

Patch:
diff --git a/DEVELOPING.md b/DEVELOPING.md
index 8346e8c..6bc48bb 100644
--- a/DEVELOPING.md
+++ b/DEVELOPING.md
@@ -19,6 +19,16 @@ make build
 
 The command will produce the `revive` binary in the root of the project.
 
+## Debug
+
+To enable debug logging, set the `DEBUG` environment variable:
+
+```sh
+DEBUG=1 go run main.go
+```
+
+This will output debug information to `stderr` and to the log file `revive.log` created in the current working directory.
+
 ## Development of rules
 
 If you want to develop a new rule, follow as an example the already existing rules in the [rule package](https://github.com/mgechev/revive/tree/master/rule).
diff --git a/RULES_DESCRIPTIONS.md b/RULES_DESCRIPTIONS.md
index ffa74b3..2b795ef 100644
--- a/RULES_DESCRIPTIONS.md
+++ b/RULES_DESCRIPTIONS.md
@@ -95,19 +95,24 @@ _Description_: Suggests using constant for [magic numbers](https://en.wikipedia.
 
 _Configuration_:
 
-* `maxLitCount` : (string) maximum number of instances of a string literal that are tolerated before warn.
-* `allowStrs`: (string) comma-separated list of allowed string literals
-* `allowInts`: (string) comma-separated list of allowed integers
-* `allowFloats`: (string) comma-separated list of allowed floats
-* `ignoreFuncs`: (string) comma-separated list of function names regexp patterns to exclude
+* `maxLitCount` (`maxlitcount`, `max-lit-count`): (string) maximum number of instances of a string literal that are tolerated before warn.
+* `allowStrs` (`allowstrs`, `allow-strs`): (string) comma-separated list of allowed string literals
+* `allowInts` (`allowints`, `allow-ints`): (string) comma-separated list of allowed integers
+* `allowFloats` (`allowfloats`, `allow-floats`): (string) comma-separated list of allowed floats
+* `ignoreFuncs` (`ignorefuncs`, `ignore-funcs`): (string) comma-separated list of function names regexp patterns to exclude
 
-Example:
+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" }]
 ```
 
+```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" }]
+```
+
 ## argument-limit
 
 _Description_: Warns when a function receives more parameters than the maximum set by the rule's configuration.
@@ -240,15 +245,20 @@ _Description_: By [convention](https://go.dev/wiki/CodeReviewComments#contexts),
 
 _Configuration_:
 
-* `allowTypesBefore` : (string) comma-separated list of types that may be before 'context.Context'
+* `allowTypesBefore` (`allowtypesbefore`, `allow-types-before`): (string) comma-separated list of types that may be before 'context.Context'
 
-Example:
+Examples:
 
 ```toml
 [rule.context-as-argument]
   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"}]
+```
+
 ## context-keys-type
 
 _Description_: Basic types should not be used as a key in `context.WithValue`.
@@ -286,20 +296,27 @@ _Description_: This rule warns on some common mistakes when using `defer` statem
 
 | name              | description                                                                                                                                                                                     |
 | ----------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
-| call-chain        | even if deferring call-chains of the form `foo()()` is valid, it does not helps code understanding (only the last call is deferred)                                                             |
+| call-chain (callChain, callchain)        | even if deferring call-chains of the form `foo()()` is valid, it does not helps code understanding (only the last call is deferred)                                                             |
 | loop              | deferring inside loops can be misleading (deferred functions are not executed at the end of the loop iteration but of the current function) and it could lead to exhausting the execution stack |
-| method-call       | deferring a call to a method can lead to subtle bugs if the method does not have a pointer receiver                                                                                             |
+| method-call (methodCall, methodcall)       | deferring a call to a method can lead to subtle bugs if the method does not have a pointer receiver                                                                                             |
 | recover           | calling `recover` outside a deferred function has no effect                                                                                                                                     |
-| immediate-recover | calling `recover` at the time a defer is registered, rather than as part of the deferred callback.  e.g. `defer recover()` or equivalent.                                                       |
+| immediate-recover (immediateRecover, immediaterecover) | calling `recover` at the time a defer is registered, rather than as part of the deferred callback.  e.g. `defer recover()` or equivalent.                                                       |
 | return            | returning values form a deferred function has no effect                                                                                                                                         |
 
 These gotchas are described [here](https://blog.learngoprogramming.com/gotchas-of-defer-in-go-1-8d070894cb01)
 
 _Configuration_: by default all warnings are enabled but it is possible selectively enable them through configuration. For example to enable only `call-chain` and `loop`:
 
+Examples:
+
+```toml
+[rule.defer]
+  arguments = [["callChain", "loop"]]
+```
+
 ```toml
 [rule.defer]
-  arguments = [["call-chain","loop"]]
+  arguments = [["call-chain", "loop"]]
 ```
 
 ## dot-imports
@@ -310,15 +327,20 @@ More information [here](https://go.dev/wiki/CodeReviewComments#import-dot)
 
 _Configuration_:
 
-* `allowedPackages`: (list of strings) comma-separated list of allowed dot import packages
+* `allowedPackages` (`allowedpackages`, `allowed-packages`): (list of strings) comma-separated list of allowed dot import packages
 
-Example:
+Examples:
 
 ```toml
 [rule.dot-imports]
   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"] }]
+```
+
 ## duplicated-imports
 
 _Description_: It is possible to unintentionally import the same package twice. This rule looks for packages that are imported two or more times.
@@ -348,16 +370,21 @@ if !cond {
 
 _Configuration_: ([]string) rule flags. Available flags are:
 
-* _preserveScope_: do not suggest refactorings that would increase variable scope
-* _allowJump_: suggest a new jump (`return`, `continue` or `break` statement) if it could unnest multiple statements. By default, only relocation of _existing_ jumps (i.e. from the `else` clause) are suggested.
+* `preserveScope` (`preservescope`, `preserve-scope`): do not suggest refactorings that would increase variable scope
+* `allowJump` (`allowjump`, `allow-jump`): suggest a new jump (`return`, `continue` or `break` statement) if it could unnest multiple statements. By default, only relocation of _existing_ jumps (i.e. from the `else` clause) are suggested.
 
-Example:
+Examples:
 
 ```toml
 [rule.early-return]
   arguments = ["preserveScope", "allowJump"]
 ```
 
+```toml
+[rule.early-return]
+  arguments = ["preserve-scope", "allow-jump"]
+```
+
 ## empty-block
 
 _Description_: Empty blocks make code less readable and could be a symptom of a bug or unfinished refactoring.
@@ -399,8 +426,8 @@ _Configuration (1)_: (string) as a single string, it configures both argument
 and return value styles. Accepts 'any', 'short', or 'full' (default: 'any').
 
 _Configuration (2)_: (map[string]any) as a map, allows separate configuration
-for function arguments and return values. Valid keys are "funcArgStyle" and
-"funcRetValStyle", each accepting 'any', 'short', or 'full'. If a key is not
+for function arguments and return values. Valid keys are `funcArgStyle` (`funcargstyle`, `func-arg-style`) and
+`funcRetValStyle` (`funcretvalstyle`, `func-ret-val-style`), each accepting 'any', 'short', or 'full'. If a key is not
 specified, the default value of 'any' is used.
 
 _Note_: The rule applies checks based on the specified styles. For 'full' style,
@@ -415,13 +442,18 @@ Example (1):
   arguments = ["short"]
 ```
 
-Example (2):
+Examples (2):
 
 ```toml
 [rule.enforce-repeated-arg-type-style]
   arguments = [{ funcArgStyle = "full", funcRetValStyle = "short" }]
 ```
 
+```toml
+[rule.enforce-repeated-arg-type-style]
+  arguments = [{ func-arg-style = "full", func-ret-val-style = "short" }]
+```
+
 ## enforce-slice-style
 
 _Description_: This rule enforces consistent usage of `make([]type, 0)`, `[]type{}`, or `var []type` for slice initialization.
@@ -487,23 +519,28 @@ _Configuration_: ([]string) rule flags.
 Please notice that without configuration, the default behavior of the rule is that of its `golint` counterpart.
 Available flags are:
 
-* _checkPrivateReceivers_ enables checking public methods of private types
-* _disableStutteringCheck_ disables checking for method names that stutter with the package name (i.e. avoid failure messages of the form _type name will be used as x.XY by other packages, and that stutters; consider calling this Y_)
-* _sayRepetitiveInsteadOfStutters_ replaces the use of the term _stutters_ by _repetitive_ in failure messages
-* _checkPublicInterface_ enabled checking public method definitions in public interface types
-* _disableChecksOnConstants_ disable all checks on constant declarations
-* _disableChecksOnFunctions_ disable all checks on function declarations
-* _disableChecksOnMethods_ disable all checks on method declarations
-* _disableChecksOnTypes_ disable all checks on type declarations
-* _disableChecksOnVariables_ disable all checks on variable declarations
+* `checkPrivateReceivers` (`checkprivatereceivers`, `check-private-receivers`) enables checking public methods of private types
+* `disableStutteringCheck` (`disablestutteringcheck`, `disable-stuttering-check`) disables checking for method names that stutter with the package name (i.e. avoid failure messages of the form _type name will be used as x.XY by other packages, and that stutters; consider calling this Y_)
+* `sayRepetitiveInsteadOfStutters` (`sayrepetitiveinsteadofstutters`, `say-repetitive-instead-of-stutters`) replaces the use of the term _stutters_ by _repetitive_ in failure messages
+* `checkPublicInterface` (`checkpublicinterface`, `check-public-interface`) enabled checking public method definitions in public interface types
+* `disableChecksOnConstants` (`disablechecksonconstants`, `disable-checks-on-constants`) disable all checks on constant declarations
+* `disableChecksOnFunctions` (`disablechecksonfunctions`, `disable-checks-on-functions`) disable all checks on function declarations
+* `disableChecksOnMethods` (`disablechecksonmethods`, `disable-checks-on-methods`) disable all checks on method declarations
+* `disableChecksOnTypes` (`disablechecksontypes`, `disable-checks-on-types`) disable all checks on type declarations
+* `disableChecksOnVariables` (`disablechecksonvariables`, `disable-checks-on-variables`) disable all checks on variable declarations
 
-Example:
+Examples:
 
 ```toml
 [rule.exported]
   arguments = ["checkPrivateReceivers", "disableStutteringCheck", "checkPublicInterface", "disableChecksOnFunctions"]
 ```
 
+```toml
+[rule.exported]
+  arguments = ["check-private-receivers", "disable-stuttering-check", "check-public-interface", "disable-checks-on-functions"]
+```
+
 ## file-header
 
 _Description_: This rule helps to enforce a common header for all source files in a project by spotting those files that do not have the specified header.
@@ -523,17 +560,22 @@ _Description_: This rule enforces a maximum number of lines per file, in order t
 
 _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`).
+* `max`: (int) a maximum number of lines in a file. Must be non-negative integers. 0 means the rule is disabled (default `0`);
+* `skipComments` (`skipcomments`, `skip-comments`): (bool) if true ignore and do not count lines containing just comments (default `false`);
+* `skipBlankLines` (`skipblanklines`, `skip-blank-lines`): (bool) if true ignore and do not count lines made up purely of whitespace (default `false`).
 
-Example:
+Examples:
 
 ```toml
 [rule.file-length-limit]
   arguments = [{max=100,skipComments=true,skipBlankLines=true}]
 ```
 
+```toml
+[rule.file-length-limit]
+  arguments = [{max=100,skip-comments=true,skip-blank-lines=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.
 
@@ -609,8 +651,8 @@ Importantly, aliases with underscores ("_") are always allowed.
 _Configuration_ (1): (`string`) as plain string accepts allow regexp pattern for aliases (default: `^[a-z][a-z0-9]{0,}$`).
 
 _Configuration_ (2): (`map[string]string`) as a map accepts two values:
-* for a key "allowRegex" accepts allow regexp pattern
-* for a key "denyRegex deny regexp pattern
+* for a key `allowRegex` (`allowregex`, `allow-regex`) accepts allow regexp pattern
+* for a key `denyRegex` (`denyregex`, `deny-regex`) deny regexp pattern
 
 _Note_: If both `allowRegex` and `denyRegex` are provided, the alias must comply with both of them.
 If none are given (i.e. an empty map), the default value `^[a-z][a-z0-9]{0,}$` for allowRegex is used.
@@ -623,13 +665,18 @@ Example (1):
   arguments = ["^[a-z][a-z0-9]{0,}$"]
 ```
 
-Example (2):
+Examples (2):
 
 ```toml
 [rule.import-alias-naming]
   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+$' }]
+```
+
 ## import-shadowing
 
 _Description_: In Go it is possible to declare identifiers (packages, structs,
@@ -667,15 +714,20 @@ More information [here](https://go.dev/wiki/CodeReviewComments#indent-error-flow
 
 _Configuration_: ([]string) rule flags. Available flags are:
 
-* _preserveScope_: do not suggest refactorings that would increase variable scope
+* `preserveScope` (`preservescope`, `preserve-scope`): do not suggest refactorings that would increase variable scope
 
-Example:
+Examples:
 
 ```toml
 [rule.indent-error-flow]
   arguments = ["preserveScope"]
 ```
 
+```toml
+[rule.indent-error-flow]
+  arguments = ["preserve-scope"]
+```
+
 ## line-length-limit
 
 _Description_: Warns in the presence of code lines longer than a configured maximum.
@@ -793,13 +845,20 @@ _Description_: By convention, receiver names in a method should reflect their id
 
 _Configuration_: (optional) list of key-value-pair-map (`[]map[string]any`).
 
-- `maxLength` : (int) max length of receiver name
+- `maxLength` (`maxlength`, `max-length`): (int) max length of receiver name
+
+Examples:
 
 ```toml
 [rule.receiver-naming]
     arguments = [{maxLength=2}]
 ```
 
+```toml
+[rule.receiver-naming]
+    arguments = [{max-length=2}]
+```
+
 ## redefines-builtin-id
 
 _Description_: Constant names like `false`, `true`, `nil`, function names like `append`, `make`, and basic type names like `bool`, and `byte` are not reserved words of the language; therefore the can be redefined.
@@ -884,15 +943,20 @@ This rule highlights redundant _else-blocks_ that can be eliminated from the cod
 
 _Configuration_: ([]string) rule flags. Available flags are:
 
-* _preserveScope_: do not suggest refactorings that would increase variable scope
+* `preserveScope` (`preservescope`, `preserve-scope`): (string) do not suggest refactorings that would increase variable scope
 
-Example:
+Examples:
 
 ```toml
 [rule.superfluous-else]
   arguments = ["preserveScope"]
 ```
 
+```toml
+[rule.superfluous-else]
+  arguments = ["preserve-scope"]
+```
+
 ## time-equal
 
 _Description_: This rule warns when using `==` and `!=` for equality check `time.Time` and suggest to `time.time.Equal` method, for about information follow this [link](https://pkg.go.dev/time#Time)
@@ -911,20 +975,25 @@ _Description_: This rule checks whether a type assertion result is checked (the
 
 _Configuration_: list of key-value-pair-map (`[]map[string]any`).
 
-- `acceptIgnoredAssertionResult` : (bool) default `false`, set it to `true` to accept ignored type assertion results like this:
+- `acceptIgnoredAssertionResult` (`acceptignoredassertionresult`, `accept-ignored-assertion-result`): (bool) default `false`, set it to `true` to accept ignored type assertion results like this:
 
 ```go
 foo, _ := bar(.*Baz).
 //   ^
 ```
 
-Example:
+Examples:
 
 ```toml
 [rule.unchecked-type-assertion]
   arguments = [{acceptIgnoredAssertionResult=true}]
 ```
 
+```toml
+[rule.unchecked-type-assertion]
+  arguments = [{accept-ignored-assertion-result=true}]
+```
+
 ## unconditional-recursion
 
 _Description_: Unconditional recursive calls will produce infinite recursion, thus program stack overflow. This rule detects and warns about unconditional (direct) recursive calls.
@@ -972,34 +1041,48 @@ _Configuration_: N/A
 
 _Description_: This rule warns on unused parameters. Functions or methods with unused parameters can be a symptom of an unfinished refactoring or a bug.
 
-_Configuration_: Supports arguments with single of `map[string]any` with option `allowRegex` to provide additional to `_` mask to allowed unused parameter names, for example:
+_Configuration_: Supports arguments with single of `map[string]any` with option `allowRegex` (`allowregex`, `allow-regex`) to provide additional to `_` mask to allowed unused parameter names.
+
+Examples:
+
+This allows any names starting with `_`, not just `_` itself:
+
+```go
+func SomeFunc(_someObj *MyStruct) {} // matches rule
+```
 
 ```toml
 [rule.unused-parameter]
     arguments = [{ allowRegex = "^_" }]
 ```
 
-allows any names started with `_`, not just `_` itself:
-
-```go
-func SomeFunc(_someObj *MyStruct) {} // matches rule
+```toml
+[rule.unused-parameter]
+    arguments = [{ allow-regex = "^_" }]
 ```
 
 ## unused-receiver
 
 _Description_: This rule warns on unused method receivers. Methods with unused receivers can be a symptom of an unfinished refactoring or a bug.
 
-_Configuration_: Supports arguments with single of `map[string]any` with option `allowRegex` to provide additional to `_` mask to allowed unused receiver names, for example:
+_Configuration_: Supports arguments with single of `map[string]any` with option `allowRegex` to provide additional to `_` mask to allowed unused receiver names.
+
+Examples:
+
+This allows any names starting with `_`, not just `_` itself:
+
+```go
+func (_my *MyStruct) SomeMethod() {} // matches rule
+```
 
 ```toml
 [rule.unused-receiver]
     arguments = [{ allowRegex = "^_" }]
 ```
 
-allows any names started with `_`, not just `_` itself:
-
-```go
-func (_my *MyStruct) SomeMethod() {} // matches rule
+```toml
+[rule.unused-receiver]
+    arguments = [{ allow-regex = "^_" }]
 ```
 
 ## use-any
@@ -1038,24 +1121,30 @@ _Description_: This rule warns when [initialism](https://go.dev/wiki/CodeReviewC
 _Configuration_: This rule accepts two slices of strings and one optional slice with single map with named parameters.
 (it's due to TOML hasn't "slice of any" and we keep backward compatibility with previous config version)
 First slice is an allowlist and second one is a blocklist of initialisms.
-In map, you can add "upperCaseConst=true" parameter to allow `UPPER_CASE` for `const`
+In map, you can add boolean `upperCaseConst` (`uppercaseconst`, `upper-case-const`) parameter to allow `UPPER_CASE` for `const`
+You can also add boolean `skipPackageNameChecks` (`skippackagenamechecks`, `skip-package-name-checks`) to skip package name checks.
 By default, the rule behaves exactly as the alternative in `golint` but optionally, you can relax it (see [golint/lint/issues/89](https://github.com/golang/lint/issues/89))
 
-Example:
+Examples:
 
 ```toml
 [rule.var-naming]
   arguments = [["ID"], ["VM"], [{upperCaseConst=true}]]
 ```
 
-You can also add "skipPackageNameChecks=true" to skip package name checks.
-
-Example:
+```toml
+[rule.var-naming]
+  arguments = [[], [], [{skipPackageNameChecks=true}]]
+```
 
+```toml
+[rule.var-naming]
+  arguments = [["ID"], ["VM"], [{upper-case-const=true}]]
+```
 
 ```toml
 [rule.var-naming]
-  arguments = [[], [], [{skipPackageNameChecks=true}]]
+  arguments = [[], [], [{skip-package-name-checks=true}]]
 ```
 
 ## waitgroup-by-value
diff --git a/go.mod b/go.mod
index 36cbeab..b9cf8fd 100644
--- a/go.mod
+++ b/go.mod
@@ -5,7 +5,7 @@ go 1.23.0
 toolchain go1.24.1
 
 require (
-	github.com/BurntSushi/toml v1.4.0
+	github.com/BurntSushi/toml v1.5.0
 	github.com/chavacava/garif v0.1.0
 	github.com/fatih/color v1.18.0
 	github.com/fatih/structtag v1.2.0
diff --git a/go.sum b/go.sum
index 3b720e4..192d8d8 100644
--- a/go.sum
+++ b/go.sum
@@ -1,5 +1,5 @@
-github.com/BurntSushi/toml v1.4.0 h1:kuoIxZQy2WRRk1pttg9asf+WVv6tWQuBNVmK8+nqPr0=
-github.com/BurntSushi/toml v1.4.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho=
+github.com/BurntSushi/toml v1.5.0 h1:W5quZX/G/csjUnuI8SUYlsHs9M38FC7znL0lIO+DvMg=
+github.com/BurntSushi/toml v1.5.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho=
 github.com/chavacava/garif v0.1.0 h1:2JHa3hbYf5D9dsgseMKAmc/MZ109otzgNFk5s87H9Pc=
 github.com/chavacava/garif v0.1.0/go.mod h1:XMyYCkEL58DF0oyW4qDjjnPWONs2HBqYKI+UIPD+Gww=
 github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
diff --git a/internal/ifelse/args.go b/internal/ifelse/args.go
index fc65b70..8ea1964 100644
--- a/internal/ifelse/args.go
+++ b/internal/ifelse/args.go
@@ -1,15 +1,5 @@
 package ifelse
 
-// PreserveScope is a configuration argument that prevents suggestions
-// that would enlarge variable scope
-const PreserveScope = "preserveScope"
-
-// AllowJump is a configuration argument that permits early-return to
-// suggest introducing a new jump (return, continue, etc) statement
-// to reduce nesting. By default, suggestions only bring existing jumps
-// earlier.
-const AllowJump = "allowJump"
-
 // Args contains arguments common to the early-return, indent-error-flow
 // and superfluous-else rules
 type Args struct {
diff --git a/internal/ifelse/rule.go b/internal/ifelse/rule.go
index 94f0221..799f8b8 100644
--- a/internal/ifelse/rule.go
+++ b/internal/ifelse/rule.go
@@ -10,7 +10,7 @@ import (
 // CheckFunc evaluates a rule against the given if-else chain and returns a message
 // describing the proposed refactor, along with a indicator of whether such a refactor
 // could be found.
-type CheckFunc func(Chain, Args) (string, bool)
+type CheckFunc func(Chain) (string, bool)
 
 // Apply evaluates the given Rule on if-else chains found within the given AST,
 // and returns the failures.
@@ -28,16 +28,9 @@ type CheckFunc func(Chain, Args) (string, bool)
 //
 // Only the block following "bar" is linted. This is because the rules that use this function
 // do not presently have anything to say about earlier blocks in the chain.
-func Apply(check CheckFunc, node ast.Node, target Target, args lint.Arguments) []lint.Failure {
+func Apply(check CheckFunc, node ast.Node, target Target, args Args) []lint.Failure {
 	v := &visitor{check: check, target: target}
-	for _, arg := range args {
-		switch arg {
-		case PreserveScope:
-			v.args.PreserveScope = true
-		case AllowJump:
-			v.args.AllowJump = true
-		}
-	}
+	v.args = args
 	ast.Walk(v, node)
 	return v.failures
 }
@@ -126,7 +119,7 @@ func (v *visitor) visitIf(ifStmt *ast.IfStmt, chain Chain) {
 }
 
 func (v *visitor) checkRule(ifStmt *ast.IfStmt, chain Chain) {
-	msg, found := v.check(chain, v.args)
+	msg, found := v.check(chain)
 	if !found {
 		return // passed the check
 	}
diff --git a/lint/file.go b/lint/file.go
index 69828d9..950d756 100644
--- a/lint/file.go
+++ b/lint/file.go
@@ -154,7 +154,7 @@ func (f *File) disabledIntervals(rules []Rule, mustSpecifyDisableReason bool, fa
 
 		for ruleName, disabledArr := range enabledDisabledRulesMap {
 			ruleResult := []DisabledInterval{}
-			for i := 0; i < len(disabledArr); i++ {
+			for i := range disabledArr {
 				interval := DisabledInterval{
 					RuleName: ruleName,
 					From: token.Position{
diff --git a/logging/logger.go b/logging/logger.go
index 9c6bb41..c310c15 100644
--- a/logging/logger.go
+++ b/logging/logger.go
@@ -3,40 +3,35 @@ package logging
 
 import (
 	"io"
-	"log"
+	"log/slog"
 	"os"
 )
 
-var logger *log.Logger
+const logFile = "revive.log"
+
+var logger *slog.Logger
 
 // GetLogger retrieves an instance of an application logger which outputs
 // to a file if the debug flag is enabled
-func GetLogger() (*log.Logger, error) {
+func GetLogger() (*slog.Logger, error) {
 	if logger != nil {
 		return logger, nil
 	}
 
-	var writer io.Writer
-	var err error
-
-	writer = io.Discard // by default, suppress all logging output
-	debugModeEnabled := os.Getenv("DEBUG") == "1"
-	if debugModeEnabled {
-		writer, err = os.Create("revive.log")
-		if err != nil {
-			return nil, err
-		}
+	debugModeEnabled := os.Getenv("DEBUG") != ""
+	if !debugModeEnabled {
+		// by default, suppress all logging output
+		return slog.New(slog.NewTextHandler(io.Discard, nil)), nil // TODO: change to slog.New(slog.DiscardHandler) when we switch to Go 1.24
 	}
 
-	logger = log.New(writer, "", log.LstdFlags)
-
-	if !debugModeEnabled {
-		// Clear all flags to skip log output formatting step to increase
-		// performance somewhat if we're not logging anything
-		logger.SetFlags(0)
+	fileWriter, err := os.Create(logFile)
+	if err != nil {
+		return nil, err
 	}
 
-	logger.Println("Logger initialized")
+	logger = slog.New(slog.NewTextHandler(io.MultiWriter(os.Stderr, fileWriter), nil))
+
+	logger.Info("Logger initialized", "logFile", logFile)
 
 	return logger, nil
 }
diff --git a/revivelib/core.go b/revivelib/core.go
index 31463fe..4dd760a 100755
--- a/revivelib/core.go
+++ b/revivelib/core.go
@@ -3,8 +3,10 @@ package revivelib
 
 import (
 	"fmt"
-	"log"
+	"log/slog"
+	"maps"
 	"os"
+	"slices"
 	"strings"
 
 	"github.com/mgechev/dots"
@@ -18,7 +20,7 @@ import (
 type Revive struct {
 	config       *lint.Config
 	lintingRules []lint.Rule
-	logger       *log.Logger
+	logger       *slog.Logger
 	maxOpenFiles int
 }
 
@@ -56,7 +58,7 @@ func New(
 		return nil, fmt.Errorf("initializing revive - getting lint rules: %w", err)
 	}
 
-	logger.Println("Config loaded")
+	logger.Info("Config loaded", "rules", slices.Collect(maps.Keys(conf.Rules)))
 
 	return &Revive{
 		logger:       logger,
diff --git a/rule/add_constant.go b/rule/add_constant.go
index c58c369..90abd4e 100644
--- a/rule/add_constant.go
+++ b/rule/add_constant.go
@@ -212,16 +212,16 @@ func (r *AddConstantRule) Configure(arguments lint.Arguments) error {
 	}
 	for k, v := range args {
 		kind := ""
-		switch k {
-		case "allowFloats":
+		switch {
+		case isRuleOption(k, "allowFloats"):
 			kind = kindFLOAT
 			fallthrough
-		case "allowInts":
+		case isRuleOption(k, "allowInts"):
 			if kind == "" {
 				kind = kindINT
 			}
 			fallthrough
-		case "allowStrs":
+		case isRuleOption(k, "allowStrs"):
 			if kind == "" {
 				kind = kindSTRING
 			}
@@ -230,7 +230,7 @@ func (r *AddConstantRule) Configure(arguments lint.Arguments) error {
 				return fmt.Errorf("invalid argument to the add-constant rule, string expected. Got '%v' (%T)", v, v)
 			}
 			r.allowList.add(kind, list)
-		case "maxLitCount":
+		case isRuleOption(k, "maxLitCount"):
 			sl, ok := v.(string)
 			if !ok {
 				return fmt.Errorf("invalid argument to the add-constant rule, expecting string representation of an integer. Got '%v' (%T)", v, v)
@@ -241,7 +241,7 @@ func (r *AddConstantRule) Configure(arguments lint.Arguments) error {
 				return fmt.Errorf("invalid argument to the add-constant rule, expecting string representation of an integer. Got '%v'", v)
 			}
 			r.strLitLimit = limit
-		case "ignoreFuncs":
+		case isRuleOption(k, "ignoreFuncs"):
 			excludes, ok := v.(string)
 			if !ok {
 				return fmt.Errorf("invalid argument to the ignoreFuncs parameter of add-constant rule, string expected. Got '%v' (%T)", v, v)
diff --git a/rule/context_as_argument.go b/rule/context_as_argument.go
index f6a1d8c..273d969 100644
--- a/rule/context_as_argument.go
+++ b/rule/context_as_argument.go
@@ -74,16 +74,14 @@ func (*ContextAsArgumentRule) getAllowTypesFromArguments(args lint.Arguments) (m
 			return nil, fmt.Errorf("invalid argument to the context-as-argument rule. Expecting a k,v map, got %T", args[0])
 		}
 		for k, v := range argKV {
-			switch k {
-			case "allowTypesBefore":
-				typesBefore, ok := v.(string)
-				if !ok {
-					return nil, fmt.Errorf("invalid argument to the context-as-argument.allowTypesBefore rule. Expecting a string, got %T", v)
-				}
-				allowTypesBefore = append(allowTypesBefore, strings.Split(typesBefore, ",")...)
-			default:
+			if !isRuleOption(k, "allowTypesBefore") {
 				return nil, fmt.Errorf("invalid argument to the context-as-argument rule. Unrecognized key %s", k)
 			}
+			typesBefore, ok := v.(string)
+			if !ok {
+				return nil, fmt.Errorf("invalid argument to the context-as-argument.allowTypesBefore rule. Expecting a string, got %T", v)
+			}
+			allowTypesBefore = append(allowTypesBefore, strings.Split(typesBefore, ",")...)
 		}
 	}
 
diff --git a/rule/defer.go b/rule/defer.go
index 47771f4..70213ae 100644
--- a/rule/defer.go
+++ b/rule/defer.go
@@ -7,6 +7,15 @@ import (
 	"github.com/mgechev/revive/lint"
 )
 
+var (
+	deferOptionLoop             = normalizeRuleOption("loop")
+	deferOptionCallChain        = normalizeRuleOption("callChain")
+	deferOptionMethodCall       = normalizeRuleOption("methodCall")
+	deferOptionReturn           = normalizeRuleOption("return")
+	deferOptionRecover          = normalizeRuleOption("recover")
+	deferOptionImmediateRecover = normalizeRuleOption("immediateRecover")
+)
+
 // DeferRule lints gotchas in defer statements.
 type DeferRule struct {
 	allow map[string]bool
@@ -45,12 +54,12 @@ func (*DeferRule) Name() string {
 func (*DeferRule) allowFromArgs(args lint.Arguments) (map[string]bool, error) {
 	if len(args) < 1 {
 		allow := map[string]bool{
-			"loop":              true,
-			"call-chain":        true,
-			"method-call":       true,
-			"return":            true,
-			"recover":           true,
-			"immediate-recover": true,
+			deferOptionLoop:             true,
+			deferOptionCallChain:        true,
+			deferOptionMethodCall:       true,
+			deferOptionReturn:           true,
+			deferOptionRecover:          true,
+			deferOptionImmediateRecover: true,
 		}
 
 		return allow, nil
@@ -67,7 +76,7 @@ func (*DeferRule) allowFromArgs(args lint.Arguments) (map[string]bool, error) {
 		if !ok {
 			return nil, fmt.Errorf("invalid argument '%v' for 'defer' rule. Expecting string, got %T", subcase, subcase)
 		}
-		allow[sc] = true
+		allow[normalizeRuleOption(sc)] = true
 	}
 
 	return allow, nil
@@ -94,7 +103,7 @@ func (w lintDeferRule) Visit(node ast.Node) ast.Visitor {
 		return nil
 	case *ast.ReturnStmt:
 		if len(n.Results) != 0 && w.inADefer && w.inAFuncLit {
-			w.newFailure("return in a defer function has no effect", n, 1.0, lint.FailureCategoryLogic, "return")
+			w.newFailure("return in a defer function has no effect", n, 1.0, lint.FailureCategoryLogic, deferOptionReturn)
 		}
 	case *ast.CallExpr:
 		isCallToRecover := isIdent(n.Fun, "recover")
@@ -103,13 +112,13 @@ func (w lintDeferRule) Visit(node ast.Node) ast.Visitor {
 			// func fn() { recover() }
 			//
 			// confidence is not 1 because recover can be in a function that is deferred elsewhere
-			w.newFailure("recover must be called inside a deferred function", n, 0.8, lint.FailureCategoryLogic, "recover")
+			w.newFailure("recover must be called inside a deferred function", n, 0.8, lint.FailureCategoryLogic, deferOptionRecover)
 		case w.inADefer && !w.inAFuncLit && isCallToRecover:
 			// defer helper(recover())
 			//
 			// confidence is not truly 1 because this could be in a correctly-deferred func,
 			// but it is very likely to be a misunderstanding of defer's behavior around arguments.
-			w.newFailure("recover must be called inside a deferred function, this is executing recover immediately", n, 1, lint.FailureCategoryLogic, "immediate-recover")
+			w.newFailure("recover must be called inside a deferred function, this is executing recover immediately", n, 1, lint.FailureCategoryLogic, deferOptionImmediateRecover)
 		}
 		return nil // no need to analyze the arguments of the function call
 	case *ast.DeferStmt:
@@ -118,7 +127,7 @@ func (w lintDeferRule) Visit(node ast.Node) ast.Visitor {
 			//
 			// confidence is not truly 1 because this could be in a correctly-deferred func,
 			// but normally this doesn't suppress a panic, and even if it did it would silently discard the value.
-			w.newFailure("recover must be called inside a deferred function, this is executing recover immediately", n, 1, lint.FailureCategoryLogic, "immediate-recover")
+			w.newFailure("recover must be called inside a deferred function, this is executing recover immediately", n, 1, lint.FailureCategoryLogic, deferOptionImmediateRecover)
 		}
 		w.visitSubtree(n.Call.Fun, true, false, false)
 		for _, a := range n.Call.Args {
@@ -131,17 +140,17 @@ func (w lintDeferRule) Visit(node ast.Node) ast.Visitor {
 		}
 
 		if w.inALoop {
-			w.newFailure("prefer not to defer inside loops", n, 1.0, lint.FailureCategoryBadPractice, "loop")
+			w.newFailure("prefer not to defer inside loops", n, 1.0, lint.FailureCategoryBadPractice, deferOptionLoop)
 		}
 
 		switch fn := n.Call.Fun.(type) {
 		case *ast.CallExpr:
-			w.newFailure("prefer not to defer chains of function calls", fn, 1.0, lint.FailureCategoryBadPractice, "call-chain")
+			w.newFailure("prefer not to defer chains of function calls", fn, 1.0, lint.FailureCategoryBadPractice, deferOptionCallChain)
 		case *ast.SelectorExpr:
 			if id, ok := fn.X.(*ast.Ident); ok {
 				isMethodCall := id != nil && id.Obj != nil && id.Obj.Kind == ast.Typ
 				if isMethodCall {
-					w.newFailure("be careful when deferring calls to methods without pointer receiver", fn, 0.8, lint.FailureCategoryBadPractice, "method-call")
+					w.newFailure("be careful when deferring calls to methods without pointer receiver", fn, 0.8, lint.FailureCategoryBadPractice, deferOptionMethodCall)
 				}
 			}
 		}
diff --git a/rule/dot_imports.go b/rule/dot_imports.go
index 3ee4999..bb86a73 100644
--- a/rule/dot_imports.go
+++ b/rule/dot_imports.go
@@ -50,10 +50,13 @@ func (r *DotImportsRule) Configure(arguments lint.Arguments) error {
 		return fmt.Errorf("invalid argument to the dot-imports rule. Expecting a k,v map, got %T", arguments[0])
 	}
 
-	if allowedPkgArg, ok := args["allowedPackages"]; ok {
-		pkgs, ok := allowedPkgArg.([]any)
+	for k, v := range args {
+		if !isRuleOption(k, "allowedPackages") {
+			continue
+		}
+		pkgs, ok := v.([]any)
 		if !ok {
-			return fmt.Errorf("invalid argument to the dot-imports rule, []string expected. Got '%v' (%T)", allowedPkgArg, allowedPkgArg)
+			return fmt.Errorf("invalid argument to the dot-imports rule, []string expected. Got '%v' (%T)", v, v)
 		}
 		for _, p := range pkgs {
 			pkg, ok := p.(string)
diff --git a/rule/early_return.go b/rule/early_return.go
index 41557b0..95ba067 100644
--- a/rule/early_return.go
+++ b/rule/early_return.go
@@ -9,11 +9,40 @@ import (
 
 // EarlyReturnRule finds opportunities to reduce nesting by inverting
 // the condition of an "if" block.
-type EarlyReturnRule struct{}
+type EarlyReturnRule struct {
+	// preserveScope prevents suggestions that would enlarge variable scope.
+	preserveScope bool
+	// allowJump permits early-return to suggest introducing a new jump
+	// (return, continue, etc) statement to reduce nesting.
+	// By default, suggestions only bring existing jumps earlier.
+	allowJump bool
+}
+
+// Configure validates the rule configuration, and configures the rule accordingly.
+//
+// Configuration implements the [lint.ConfigurableRule] interface.
+func (e *EarlyReturnRule) Configure(arguments lint.Arguments) error {
+	for _, arg := range arguments {
+		sarg, ok := arg.(string)
+		if !ok {
+			continue
+		}
+		switch {
+		case isRuleOption(sarg, "preserveScope"):
+			e.preserveScope = true
+		case isRuleOption(sarg, "allowJump"):
+			e.allowJump = true
+		}
+	}
+	return nil
+}
 
 // Apply applies the rule to given file.
-func (e *EarlyReturnRule) Apply(file *lint.File, args lint.Arguments) []lint.Failure {
-	return ifelse.Apply(e.checkIfElse, file.AST, ifelse.TargetIf, args)
+func (e *EarlyReturnRule) Apply(file *lint.File, _ lint.Arguments) []lint.Failure {
+	return ifelse.Apply(e.checkIfElse, file.AST, ifelse.TargetIf, ifelse.Args{
+		PreserveScope: e.preserveScope,
+		AllowJump:     e.allowJump,
+	})
 }
 
 // Name returns the rule name.
@@ -21,13 +50,13 @@ func (*EarlyReturnRule) Name() string {
 	return "early-return"
 }
 
-func (*EarlyReturnRule) checkIfElse(chain ifelse.Chain, args ifelse.Args) (string, bool) {
+func (e *EarlyReturnRule) checkIfElse(chain ifelse.Chain) (string, bool) {
 	if chain.HasElse {
 		if !chain.Else.BranchKind.Deviates() {
 			// this rule only applies if the else-block deviates control flow
 			return "", false
 		}
-	} else if !args.AllowJump || !chain.AtBlockEnd || !chain.BlockEndKind.Deviates() || chain.If.IsShort() {
+	} else if !e.allowJump || !chain.AtBlockEnd || !chain.BlockEndKind.Deviates() || chain.If.IsShort() {
 		// this kind of refactor requires introducing a new indented "return", "continue" or "break" statement,
 		// so ignore unless we are able to outdent multiple statements in exchange.
 		return "", false
@@ -44,7 +73,7 @@ func (*EarlyReturnRule) checkIfElse(chain ifelse.Chain, args ifelse.Args) (strin
 		return "", false
 	}
 
-	if args.PreserveScope && !chain.AtBlockEnd && (chain.HasInitializer || chain.If.HasDecls()) {
+	if e.preserveScope && !chain.AtBlockEnd && (chain.HasInitializer || chain.If.HasDecls()) {
 		// avoid increasing variable scope
 		return "", false
 	}
diff --git a/rule/enforce_repeated_arg_type_style.go b/rule/enforce_repeated_arg_type_style.go
index ab466f5..8a5d9b3 100644
--- a/rule/enforce_repeated_arg_type_style.go
+++ b/rule/enforce_repeated_arg_type_style.go
@@ -69,8 +69,8 @@ func (r *EnforceRepeatedArgTypeStyleRule) Configure(arguments lint.Arguments) er
 		r.funcRetValStyle = valstyle
 	case map[string]any: // expecting map[string]string
 		for k, v := range funcArgStyle {
-			switch k {
-			case "funcArgStyle":
+			switch {
+			case isRuleOption(k, "funcArgStyle"):
 				val, ok := v.(string)
 				if !ok {
 					return fmt.Errorf("invalid map value type for 'enforce-repeated-arg-type-style' rule. Expecting string, got %T", v)
@@ -80,7 +80,7 @@ func (r *EnforceRepeatedArgTypeStyleRule) Configure(arguments lint.Arguments) er
 					return err
 				}
 				r.funcArgStyle = valstyle
-			case "funcRetValStyle":
+			case isRuleOption(k, "funcRetValStyle"):
 				val, ok := v.(string)
 				if !ok {
 					return fmt.Errorf("invalid map value '%v' for 'enforce-repeated-arg-type-style' rule. Expecting string, got %T", v, v)
diff --git a/rule/exported.go b/rule/exported.go
index 7d59c4d..503e25a 100644
--- a/rule/exported.go
+++ b/rule/exported.go
@@ -78,24 +78,24 @@ func (r *ExportedRule) Configure(arguments lint.Arguments) error {
 	for _, flag := range arguments {
 		switch flag := flag.(type) {
 		case string:
-			switch flag {
-			case "checkPrivateReceivers":
+			switch {
+			case isRuleOption(flag, "checkPrivateReceivers"):
 				r.disabledChecks.PrivateReceivers = false
-			case "disableStutteringCheck":
+			case isRuleOption(flag, "disableStutteringCheck"):
 				r.disabledChecks.Stuttering = true
-			case "sayRepetitiveInsteadOfStutters":
+			case isRuleOption(flag, "sayRepetitiveInsteadOfStutters"):
 				r.stuttersMsg = "is repetitive"
-			case "checkPublicInterface":
+			case isRuleOption(flag, "checkPublicInterface"):
 				r.disabledChecks.PublicInterfaces = false
-			case "disableChecksOnConstants":
+			case isRuleOption(flag, "disableChecksOnConstants"):
 				r.disabledChecks.Const = true
-			case "disableChecksOnFunctions":
+			case isRuleOption(flag, "disableChecksOnFunctions"):
 				r.disabledChecks.Function = true
-			case "disableChecksOnMethods":
+			case isRuleOption(flag, "disableChecksOnMethods"):
 				r.disabledChecks.Method = true
-			case "disableChecksOnTypes":
+			case isRuleOption(flag, "disableChecksOnTypes"):
 				r.disabledChecks.Type = true
-			case "disableChecksOnVariables":
+			case isRuleOption(flag, "disableChecksOnVariables"):
 				r.disabledChecks.Var = true
 			default:
 				return fmt.Errorf("unknown configuration flag %s for %s rule", flag, r.Name())
diff --git a/rule/file_length_limit.go b/rule/file_length_limit.go
index 8a3b371..a9b4e8d 100644
--- a/rule/file_length_limit.go
+++ b/rule/file_length_limit.go
@@ -83,23 +83,23 @@ func (r *FileLengthLimitRule) Configure(arguments lint.Arguments) error {
 		return fmt.Errorf(`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":
+		switch {
+		case isRuleOption(k, "max"):
 			maxLines, ok := v.(int64)
 			if !ok || maxLines < 0 {
-				return fmt.Errorf(`invalid configuration value for max lines in "file-length-limit" rule; need positive int64 but got %T`, arguments[0])
+				return fmt.Errorf(`invalid configuration value for max lines in "file-length-limit" rule; need positive int64 but got %T`, v)
 			}
 			r.max = int(maxLines)
-		case "skipComments":
+		case isRuleOption(k, "skipComments"):
 			skipComments, ok := v.(bool)
 			if !ok {
-				return fmt.Errorf(`invalid configuration value for skip comments in "file-length-limit" rule; need bool but got %T`, arguments[1])
+				return fmt.Errorf(`invalid configuration value for skip comments in "file-length-limit" rule; need bool but got %T`, v)
 			}
 			r.skipComments = skipComments
-		case "skipBlankLines":
+		case isRuleOption(k, "skipBlankLines"):
 			skipBlankLines, ok := v.(bool)
 			if !ok {
-				return fmt.Errorf(`invalid configuration value for skip blank lines in "file-length-limit" rule; need bool but got %T`, arguments[2])
+				return fmt.Errorf(`invalid configuration value for skip blank lines in "file-length-limit" rule; need bool but got %T`, v)
 			}
 			r.skipBlankLines = skipBlankLines
 		}
diff --git a/rule/if_return.go b/rule/if_return.go
index f9e5ef2..d0f581f 100644
--- a/rule/if_return.go
+++ b/rule/if_return.go
@@ -38,7 +38,7 @@ type lintElseError struct {
 func (w *lintElseError) Visit(node ast.Node) ast.Visitor {
 	switch v := node.(type) {
 	case *ast.BlockStmt:
-		for i := 0; i < len(v.List)-1; i++ {
+		for i := range len(v.List) - 1 {
 			// if var := whatever; var != nil { return var }
 			s, ok := v.List[i].(*ast.IfStmt)
 			if !ok || s.Body == nil || len(s.Body.List) != 1 || s.Else != nil {
diff --git a/rule/import_alias_naming.go b/rule/import_alias_naming.go
index 0859150..94cea78 100644
--- a/rule/import_alias_naming.go
+++ b/rule/import_alias_naming.go
@@ -34,13 +34,13 @@ func (r *ImportAliasNamingRule) Configure(arguments lint.Arguments) error {
 		}
 	case map[string]any: // expecting map[string]string
 		for k, v := range namingRule {
-			switch k {
-			case "allowRegex":
+			switch {
+			case isRuleOption(k, "allowRegex"):
 				err := r.setAllowRule(v)
 				if err != nil {
 					return err
 				}
-			case "denyRegex":
+			case isRuleOption(k, "denyRegex"):
 				err := r.setDenyRule(v)
 				if err != nil {
 					return err
diff --git a/rule/indent_error_flow.go b/rule/indent_error_flow.go
index 4dda64c..be4734b 100644
--- a/rule/indent_error_flow.go
+++ b/rule/indent_error_flow.go
@@ -6,11 +6,33 @@ import (
 )
 
 // IndentErrorFlowRule prevents redundant else statements.
-type IndentErrorFlowRule struct{}
+type IndentErrorFlowRule struct {
+	// preserveScope prevents suggestions that would enlarge variable scope.
+	preserveScope bool
+}
+
+// Configure validates the rule configuration, and configures the rule accordingly.
+//
+// Configuration implements the [lint.ConfigurableRule] interface.
+func (e *IndentErrorFlowRule) Configure(arguments lint.Arguments) error {
+	for _, arg := range arguments {
+		sarg, ok := arg.(string)
+		if !ok {
+			continue
+		}
+		if isRuleOption(sarg, "preserveScope") {
+			e.preserveScope = true
+		}
+	}
+	return nil
+}
 
 // Apply applies the rule to given file.
-func (e *IndentErrorFlowRule) Apply(file *lint.File, args lint.Arguments) []lint.Failure {
-	return ifelse.Apply(e.checkIfElse, file.AST, ifelse.TargetElse, args)
+func (e *IndentErrorFlowRule) Apply(file *lint.File, _ lint.Arguments) []lint.Failure {
+	return ifelse.Apply(e.checkIfElse, file.AST, ifelse.TargetElse, ifelse.Args{
+		PreserveScope: e.preserveScope,
+		// AllowJump is not used by this rule
+	})
 }
 
 // Name returns the rule name.
@@ -18,7 +40,7 @@ func (*IndentErrorFlowRule) Name() string {
 	return "indent-error-flow"
 }
 
-func (*IndentErrorFlowRule) checkIfElse(chain ifelse.Chain, args ifelse.Args) (string, bool) {
+func (e *IndentErrorFlowRule) checkIfElse(chain ifelse.Chain) (string, bool) {
 	if !chain.HasElse {
 		return "", false
 	}
@@ -39,7 +61,7 @@ func (*IndentErrorFlowRule) checkIfElse(chain ifelse.Chain, args ifelse.Args) (s
 		return "", false
 	}
 
-	if args.PreserveScope && !chain.AtBlockEnd && (chain.HasInitializer || chain.Else.HasDecls()) {
+	if e.preserveScope && !chain.AtBlockEnd && (chain.HasInitializer || chain.Else.HasDecls()) {
 		// avoid increasing variable scope
 		return "", false
 	}
diff --git a/rule/receiver_naming.go b/rule/receiver_naming.go
index 0327fee..e2737ed 100644
--- a/rule/receiver_naming.go
+++ b/rule/receiver_naming.go
@@ -29,16 +29,14 @@ func (r *ReceiverNamingRule) Configure(arguments lint.Arguments) error {
 	}
 
 	for k, v := range args {
-		switch k {
-		case "maxLength":
-			value, ok := v.(int64)
-			if !ok {
-				return fmt.Errorf("invalid value %v for argument %s of rule %s, expected integer value got %T", v, k, r.Name(), v)
-			}
-			r.receiverNameMaxLength = int(value)
-		default:
+		if !isRuleOption(k, "maxLength") {
 			return fmt.Errorf("unknown argument %s for %s rule", k, r.Name())
 		}
+		value, ok := v.(int64)
+		if !ok {
+			return fmt.Errorf("invalid value %v for argument %s of rule %s, expected integer value got %T", v, k, r.Name(), v)
+		}
+		r.receiverNameMaxLength = int(value)
 	}
 	return nil
 }
diff --git a/rule/struct_tag.go b/rule/struct_tag.go
index 2115d81..d6a7708 100644
--- a/rule/struct_tag.go
+++ b/rule/struct_tag.go
@@ -3,6 +3,7 @@ package rule
 import (
 	"fmt"
 	"go/ast"
+	"slices"
 	"strconv"
 	"strings"
 
@@ -574,12 +575,7 @@ func (w lintStructTagRule) isUserDefined(key, opt string) bool {
 	}
 
 	options := w.userDefined[key]
-	for _, o := range options {
-		if opt == o {
-			return true
-		}
-	}
-	return false
+	return slices.Contains(options, opt)
 }
 
 func areValidateOpts(opts string) (string, bool) {
diff --git a/rule/superfluous_else.go b/rule/superfluous_else.go
index c9474d9..dfa8a2f 100644
--- a/rule/superfluous_else.go
+++ b/rule/superfluous_else.go
@@ -8,11 +8,33 @@ import (
 )
 
 // SuperfluousElseRule lints given else constructs.
-type SuperfluousElseRule struct{}
+type SuperfluousElseRule struct {
+	// preserveScope prevents suggestions that would enlarge variable scope.
+	preserveScope bool
+}
+
+// Configure validates the rule configuration, and configures the rule accordingly.
+//
+// Configuration implements the [lint.ConfigurableRule] interface.
+func (e *SuperfluousElseRule) Configure(arguments lint.Arguments) error {
+	for _, arg := range arguments {
+		sarg, ok := arg.(string)
+		if !ok {
+			continue
+		}
+		if isRuleOption(sarg, "preserveScope") {
+			e.preserveScope = true
+		}
+	}
+	return nil
+}
 
 // Apply applies the rule to given file.
-func (e *SuperfluousElseRule) Apply(file *lint.File, args lint.Arguments) []lint.Failure {
-	return ifelse.Apply(e.checkIfElse, file.AST, ifelse.TargetElse, args)
+func (e *SuperfluousElseRule) Apply(file *lint.File, _ lint.Arguments) []lint.Failure {
+	return ifelse.Apply(e.checkIfElse, file.AST, ifelse.TargetElse, ifelse.Args{
+		PreserveScope: e.preserveScope,
+		// AllowJump is not used by this rule
+	})
 }
 
 // Name returns the rule name.
@@ -20,7 +42,7 @@ func (*SuperfluousElseRule) Name() string {
 	return "superfluous-else"
 }
 
-func (*SuperfluousElseRule) checkIfElse(chain ifelse.Chain, args ifelse.Args) (string, bool) {
+func (e *SuperfluousElseRule) checkIfElse(chain ifelse.Chain) (string, bool) {
 	if !chain.HasElse {
 		return "", false
 	}
@@ -41,7 +63,7 @@ func (*SuperfluousElseRule) checkIfElse(chain ifelse.Chain, args ifelse.Args) (s
 		return "", false
 	}
 
-	if args.PreserveScope && !chain.AtBlockEnd && (chain.HasInitializer || chain.Else.HasDecls()) {
+	if e.preserveScope && !chain.AtBlockEnd && (chain.HasInitializer || chain.Else.HasDecls()) {
 		// avoid increasing variable scope
 		return "", false
 	}
diff --git a/rule/unchecked_type_assertion.go b/rule/unchecked_type_assertion.go
index a272724..eb0ba4f 100644
--- a/rule/unchecked_type_assertion.go
+++ b/rule/unchecked_type_assertion.go
@@ -32,15 +32,13 @@ func (r *UncheckedTypeAssertionRule) Configure(arguments lint.Arguments) error {
 	}
 
 	for k, v := range args {
-		switch k {
-		case "acceptIgnoredAssertionResult":
-			r.acceptIgnoredAssertionResult, ok = v.(bool)
-			if !ok {
-				return fmt.Errorf("unable to parse argument '%s'. Expected boolean", k)
-			}
-		default:
+		if !isRuleOption(k, "acceptIgnoredAssertionResult") {
 			return fmt.Errorf("unknown argument: %s", k)
 		}
+		r.acceptIgnoredAssertionResult, ok = v.(bool)
+		if !ok {
+			return fmt.Errorf("unable to parse argument '%s'. Expected boolean", k)
+		}
 	}
 	return nil
 }
diff --git a/rule/unhandled_error.go b/rule/unhandled_error.go
index 6ae919b..6e2fb2d 100644
--- a/rule/unhandled_error.go
+++ b/rule/unhandled_error.go
@@ -151,7 +151,7 @@ func (*lintUnhandledErrors) isTypeError(t *types.Named) bool {
 }
 
 func (w *lintUnhandledErrors) returnsAnError(tt *types.Tuple) bool {
-	for i := 0; i < tt.Len(); i++ {
+	for i := range tt.Len() {
 		nt, ok := tt.At(i).Type().(*types.Named)
 		if ok && w.isTypeError(nt) {
 			return true
diff --git a/rule/unused_param.go b/rule/unused_param.go
index 7131f5d..826eca1 100644
--- a/rule/unused_param.go
+++ b/rule/unused_param.go
@@ -28,24 +28,28 @@ func (r *UnusedParamRule) Configure(args lint.Arguments) error {
 	if len(args) == 0 {
 		return nil
 	}
-	// Arguments = [{}]
-	options := args[0].(map[string]any)
 
-	allowRegexParam, ok := options["allowRegex"]
+	options, ok := args[0].(map[string]any)
 	if !ok {
+		// Arguments = [{}]
 		return nil
 	}
-	// Arguments = [{allowRegex="^_"}]
-	allowRegexStr, ok := allowRegexParam.(string)
-	if !ok {
-		return fmt.Errorf("error configuring %s rule: allowRegex is not string but [%T]", r.Name(), allowRegexParam)
-	}
-	var err error
-	r.allowRegex, err = regexp.Compile(allowRegexStr)
-	if err != nil {
-		return fmt.Errorf("error configuring %s rule: allowRegex is not valid regex [%s]: %w", r.Name(), allowRegexStr, err)
+	for k, v := range options {
+		if !isRuleOption(k, "allowRegex") {
+			return nil
+		}
+		// Arguments = [{allowRegex="_"}]
+		allowRegexStr, ok := v.(string)
+		if !ok {
+			return fmt.Errorf("error configuring %s rule: allowRegex is not string but [%T]", r.Name(), v)
+		}
+		var err error
+		r.allowRegex, err = regexp.Compile(allowRegexStr)
+		if err != nil {
+			return fmt.Errorf("error configuring %s rule: allowRegex is not valid regex [%s]: %w", r.Name(), allowRegexStr, err)
+		}
+		r.failureMsg = "parameter '%s' seems to be unused, consider removing or renaming it to match " + r.allowRegex.String()
 	}
-	r.failureMsg = "parameter '%s' seems to be unused, consider removing or renaming it to match " + r.allowRegex.String()
 	return nil
 }
 
diff --git a/rule/unused_receiver.go b/rule/unused_receiver.go
index 0713d07..2b39296 100644
--- a/rule/unused_receiver.go
+++ b/rule/unused_receiver.go
@@ -27,23 +27,27 @@ func (r *UnusedReceiverRule) Configure(args lint.Arguments) error {
 		return nil
 	}
 	// Arguments = [{}]
-	options := args[0].(map[string]any)
-
-	allowRegexParam, ok := options["allowRegex"]
+	options, ok := args[0].(map[string]any)
 	if !ok {
 		return nil
 	}
-	// Arguments = [{allowRegex="^_"}]
-	allowRegexStr, ok := allowRegexParam.(string)
-	if !ok {
-		return fmt.Errorf("error configuring [unused-receiver] rule: allowRegex is not string but [%T]", allowRegexParam)
-	}
-	var err error
-	r.allowRegex, err = regexp.Compile(allowRegexStr)
-	if err != nil {
-		return fmt.Errorf("error configuring [unused-receiver] rule: allowRegex is not valid regex [%s]: %w", allowRegexStr, err)
+
+	for k, v := range options {
+		if !isRuleOption(k, "allowRegex") {
+			return nil
+		}
+		// Arguments = [{allowRegex="^_"}]
+		allowRegexStr, ok := v.(string)
+		if !ok {
+			return fmt.Errorf("error configuring [unused-receiver] rule: allowRegex is not string but [%T]", v)
+		}
+		var err error
+		r.allowRegex, err = regexp.Compile(allowRegexStr)
+		if err != nil {
+			return fmt.Errorf("error configuring [unused-receiver] rule: allowRegex is not valid regex [%s]: %w", allowRegexStr, err)
+		}
+		r.failureMsg = "method receiver '%s' is not referenced in method's body, consider removing or renaming it to match " + r.allowRegex.String()
 	}
-	r.failureMsg = "method receiver '%s' is not referenced in method's body, consider removing or renaming it to match " + r.allowRegex.String()
 	return nil
 }
 
diff --git a/rule/utils.go b/rule/utils.go
index 5075c78..05f4c04 100644
--- a/rule/utils.go
+++ b/rule/utils.go
@@ -7,6 +7,7 @@ import (
 	"go/printer"
 	"go/token"
 	"regexp"
+	"strings"
 
 	"github.com/mgechev/revive/lint"
 )
@@ -112,6 +113,18 @@ func checkNumberOfArguments(expected int, args lint.Arguments, ruleName string)
 	return nil
 }
 
+// isRuleOption returns true if arg and name are the same after normalization.
+func isRuleOption(arg, name string) bool {
+	return normalizeRuleOption(arg) == normalizeRuleOption(name)
+}
+
+// normalizeRuleOption returns an option name from the argument. It is lowercased and without hyphens.
+//
+// Example: normalizeRuleOption("allowTypesBefore"), normalizeRuleOption("allow-types-before") -> "allowtypesbefore"
+func normalizeRuleOption(arg string) string {
+	return strings.ToLower(strings.ReplaceAll(arg, "-", ""))
+}
+
 var directiveCommentRE = regexp.MustCompile("^//(line |extern |export |[a-z0-9]+:[a-z0-9])") // see https://go-review.googlesource.com/c/website/+/442516/1..2/_content/doc/comment.md#494
 
 func isDirectiveComment(line string) bool {
diff --git a/rule/var_naming.go b/rule/var_naming.go
index bffcbb2..2248d35 100644
--- a/rule/var_naming.go
+++ b/rule/var_naming.go
@@ -64,8 +64,14 @@ func (r *VarNamingRule) Configure(arguments lint.Arguments) error {
 		if !ok {
 			return fmt.Errorf("invalid third argument to the var-naming rule. Expecting a %s of type slice, of len==1, with map, but %T", "options", asSlice[0])
 		}
-		r.allowUpperCaseConst = fmt.Sprint(args["upperCaseConst"]) == "true"
-		r.skipPackageNameChecks = fmt.Sprint(args["skipPackageNameChecks"]) == "true"
+		for k, v := range args {
+			switch {
+			case isRuleOption(k, "upperCaseConst"):
+				r.allowUpperCaseConst = fmt.Sprint(v) == "true"
+			case isRuleOption(k, "skipPackageNameChecks"):
+				r.skipPackageNameChecks = fmt.Sprint(v) == "true"
+			}
+		}
 	}
 	return nil
 }
@@ -279,7 +285,7 @@ func getList(arg any, argName string) ([]string, error) {
 	for _, v := range args {
 		val, ok := v.(string)
 		if !ok {
-			return nil, fmt.Errorf("invalid %s values of the var-naming rule. Expecting slice of strings but got element of type %T", val, arg)
+			return nil, fmt.Errorf("invalid %v values of the var-naming rule. Expecting slice of strings but got element of type %T", v, arg)
 		}
 		list = append(list, val)
 	}


Test patch:
diff --git a/logging/logger_test.go b/logging/logger_test.go
new file mode 100644
index 0000000..3dfeb85
--- /dev/null
+++ b/logging/logger_test.go
@@ -0,0 +1,60 @@
+package logging_test
+
+import (
+	"os"
+	"testing"
+
+	"github.com/mgechev/revive/logging"
+)
+
+func TestGetLogger(t *testing.T) {
+	t.Run("no debug", func(t *testing.T) {
+		t.Setenv("DEBUG", "")
+
+		logger, err := logging.GetLogger()
+
+		if err != nil {
+			t.Fatalf("expected no error, got %v", err)
+		}
+		if logger == nil {
+			t.Fatal("expected logger to be non-nil")
+		}
+		logger.Info("msg") // no output
+	})
+
+	t.Run("debug", func(t *testing.T) {
+		t.Setenv("DEBUG", "1")
+		t.Cleanup(func() { os.Remove("revive.log") })
+
+		logger, err := logging.GetLogger()
+
+		if err != nil {
+			t.Fatalf("expected no error, got %v", err)
+		}
+		if logger == nil {
+			t.Fatal("expected logger to be non-nil")
+		}
+		if _, err := os.Stat("revive.log"); os.IsNotExist(err) {
+			t.Error("expected revive.log file to be created")
+		}
+	})
+
+	t.Run("reuse logger", func(t *testing.T) {
+		t.Setenv("DEBUG", "1")
+		t.Cleanup(func() { os.Remove("revive.log") })
+
+		logger1, err := logging.GetLogger()
+		if err != nil {
+			t.Fatalf("expected no error, got %v", err)
+		}
+
+		logger2, err := logging.GetLogger()
+		if err != nil {
+			t.Fatalf("expected no error, got %v", err)
+		}
+
+		if logger1 != logger2 {
+			t.Errorf("expected the same logger instance to be returned: logger1=%+v, logger2=%+v", logger1, logger2)
+		}
+	})
+}
diff --git a/rule/add_constant_test.go b/rule/add_constant_test.go
new file mode 100644
index 0000000..c6e4d92
--- /dev/null
+++ b/rule/add_constant_test.go
@@ -0,0 +1,193 @@
+package rule
+
+import (
+	"errors"
+	"reflect"
+	"testing"
+
+	"github.com/mgechev/revive/lint"
+)
+
+func TestAddConstantRule_Configure(t *testing.T) {
+	tests := []struct {
+		name            string
+		arguments       lint.Arguments
+		wantErr         error
+		wantList        allowList
+		wantStrLitLimit int
+	}{
+		{
+
+			name:      "no arguments",
+			arguments: lint.Arguments{},
+			wantErr:   nil,
+			wantList: allowList{
+				kindINT:    {},
+				kindFLOAT:  {},
+				kindSTRING: {},
+			},
+			wantStrLitLimit: 2,
+		},
+		{
+			name: "valid arguments",
+			arguments: lint.Arguments{
+				map[string]any{
+					"allowFloats": "1.0,2.0",
+					"allowInts":   "1,2",
+					"allowStrs":   "a,b",
+					"maxLitCount": "3",
+					"ignoreFuncs": "fmt.Println,fmt.Printf",
+				},
+			},
+			wantErr: nil,
+			wantList: allowList{
+				kindFLOAT:  {"1.0": true, "2.0": true},
+				kindINT:    {"1": true, "2": true},
+				kindSTRING: {"a": true, "b": true},
+			},
+			wantStrLitLimit: 3,
+		},
+		{
+			name: "valid lowercased arguments",
+			arguments: lint.Arguments{
+				map[string]any{
+					"allowfloats": "1.0,2.0",
+					"allowints":   "1,2",
+					"allowstrs":   "a,b",
+					"maxlitcount": "3",
+					"ignorefuncs": "fmt.Println,fmt.Printf",
+				},
+			},
+			wantErr: nil,
+			wantList: allowList{
+				kindFLOAT:  {"1.0": true, "2.0": true},
+				kindINT:    {"1": true, "2": true},
+				kindSTRING: {"a": true, "b": true},
+			},
+			wantStrLitLimit: 3,
+		},
+		{
+			name: "valid kebab-cased arguments",
+			arguments: lint.Arguments{
+				map[string]any{
+					"allow-floats":  "1.0,2.0",
+					"allow-ints":    "1,2",
+					"allow-strs":    "a,b",
+					"max-lit-count": "3",
+					"ignore-funcs":  "fmt.Println,fmt.Printf",
+				},
+			},
+			wantErr: nil,
+			wantList: allowList{
+				kindFLOAT:  {"1.0": true, "2.0": true},
+				kindINT:    {"1": true, "2": true},
+				kindSTRING: {"a": true, "b": true},
+			},
+			wantStrLitLimit: 3,
+		},
+		{
+			name: "unrecognized key",
+			arguments: lint.Arguments{
+				map[string]any{
+					"unknownKey": "someValue",
+				},
+			},
+			wantErr: nil,
+			wantList: allowList{
+				kindINT:    {},
+				kindFLOAT:  {},
+				kindSTRING: {},
+			},
+			wantStrLitLimit: 2,
+		},
+		{
+			name: "invalid argument type",
+			arguments: lint.Arguments{
+				"invalid_argument",
+			},
+			wantErr: errors.New("invalid argument to the add-constant rule, expecting a k,v map. Got string"),
+		},
+		{
+			name: "invalid allowFloats value",
+			arguments: lint.Arguments{
+				map[string]any{
+					"allowFloats": 123,
+				},
+			},
+			wantErr: errors.New("invalid argument to the add-constant rule, string expected. Got '123' (int)"),
+		},
+		{
+			name: "invalid maxLitCount value: not a string",
+			arguments: lint.Arguments{
+				map[string]any{
+					"maxLitCount": 123,
+				},
+			},
+			wantErr: errors.New("invalid argument to the add-constant rule, expecting string representation of an integer. Got '123' (int)"),
+		},
+		{
+			name: "invalid maxLitCount value: not an int",
+			arguments: lint.Arguments{
+				map[string]any{
+					"maxLitCount": "abc",
+				},
+			},
+			wantErr: errors.New("invalid argument to the add-constant rule, expecting string representation of an integer. Got 'abc'"),
+		},
+		{
+			name: "invalid ignoreFuncs value: not a string",
+			arguments: lint.Arguments{
+				map[string]any{
+					"ignoreFuncs": 123,
+				},
+			},
+			wantErr: errors.New("invalid argument to the ignoreFuncs parameter of add-constant rule, string expected. Got '123' (int)"),
+		},
+		{
+			name: "invalid ignoreFuncs value: empty string",
+			arguments: lint.Arguments{
+				map[string]any{
+					"ignoreFuncs": " ",
+				},
+			},
+			wantErr: errors.New("invalid argument to the ignoreFuncs parameter of add-constant rule, expected regular expression must not be empty"),
+		},
+		{
+			name: "invalid ignoreFuncs value: wrong regexp",
+			arguments: lint.Arguments{
+				map[string]any{
+					"ignoreFuncs": "(",
+				},
+			},
+			wantErr: errors.New(`invalid argument to the ignoreFuncs parameter of add-constant rule: regexp "(" does not compile: error parsing regexp: missing closing ): ` + "`(`"),
+		},
+	}
+
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			var rule AddConstantRule
+
+			err := rule.Configure(tt.arguments)
+
+			if tt.wantErr != nil {
+				if err == nil {
+					t.Errorf("unexpected error: got = nil, want = %v", tt.wantErr)
+					return
+				}
+				if err.Error() != tt.wantErr.Error() {
+					t.Errorf("unexpected error: got = %v, want = %v", err, tt.wantErr)
+				}
+				return
+			}
+			if err != nil {
+				t.Errorf("unexpected error: got = %v, want = nil", err)
+			}
+			if !reflect.DeepEqual(rule.allowList, tt.wantList) {
+				t.Errorf("unexpected allowList: got = %v, want %v", rule.allowList, tt.wantList)
+			}
+			if rule.strLitLimit != tt.wantStrLitLimit {
+				t.Errorf("unexpected strLitLimit: got = %v, want %v", rule.strLitLimit, tt.wantStrLitLimit)
+			}
+		})
+	}
+}
diff --git a/rule/context_as_argument_test.go b/rule/context_as_argument_test.go
new file mode 100644
index 0000000..13638e7
--- /dev/null
+++ b/rule/context_as_argument_test.go
@@ -0,0 +1,117 @@
+package rule
+
+import (
+	"errors"
+	"reflect"
+	"testing"
+
+	"github.com/mgechev/revive/lint"
+)
+
+func TestContextAsArgumentRule_Configure(t *testing.T) {
+	tests := []struct {
+		name      string
+		arguments lint.Arguments
+		wantErr   error
+		wantTypes map[string]struct{}
+	}{
+		{
+			name: "valid arguments",
+			arguments: lint.Arguments{
+				map[string]any{
+					"allowTypesBefore": "AllowedBeforeType,AllowedBeforeStruct,*AllowedBeforePtrStruct,*testing.T",
+				},
+			},
+			wantErr: nil,
+			wantTypes: map[string]struct{}{
+				"context.Context":         {},
+				"AllowedBeforeType":       {},
+				"AllowedBeforeStruct":     {},
+				"*AllowedBeforePtrStruct": {},
+				"*testing.T":              {},
+			},
+		},
+		{
+			name: "valid lowercased arguments",
+			arguments: lint.Arguments{
+				map[string]any{
+					"allowtypesbefore": "AllowedBeforeType,AllowedBeforeStruct,*AllowedBeforePtrStruct,*testing.T",
+				},
+			},
+			wantErr: nil,
+			wantTypes: map[string]struct{}{
+				"context.Context":         {},
+				"AllowedBeforeType":       {},
+				"AllowedBeforeStruct":     {},
+				"*AllowedBeforePtrStruct": {},
+				"*testing.T":              {},
+			},
+		},
+		{
+			name: "valid kebab-cased arguments",
+			arguments: lint.Arguments{
+				map[string]any{
+					"allow-types-before": "AllowedBeforeType,AllowedBeforeStruct,*AllowedBeforePtrStruct,*testing.T",
+				},
+			},
+			wantErr: nil,
+			wantTypes: map[string]struct{}{
+				"context.Context":         {},
+				"AllowedBeforeType":       {},
+				"AllowedBeforeStruct":     {},
+				"*AllowedBeforePtrStruct": {},
+				"*testing.T":              {},
+			},
+		},
+		{
+			name: "invalid argument type",
+			arguments: lint.Arguments{
+				"invalid_argument",
+			},
+			wantErr: errors.New("invalid argument to the context-as-argument rule. Expecting a k,v map, got string"),
+		},
+		{
+			name: "invalid allowTypesBefore value",
+			arguments: lint.Arguments{
+				map[string]any{
+					"allowTypesBefore": 123,
+				},
+			},
+			wantErr: errors.New("invalid argument to the context-as-argument.allowTypesBefore rule. Expecting a string, got int"),
+		},
+		{
+			name: "unrecognized key",
+			arguments: lint.Arguments{
+				map[string]any{
+					"unknownKey": "someValue",
+				},
+			},
+			wantErr: errors.New("invalid argument to the context-as-argument rule. Unrecognized key unknownKey"),
+		},
+	}
+
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			var rule ContextAsArgumentRule
+
+			err := rule.Configure(tt.arguments)
+
+			if tt.wantErr != nil {
+				if err == nil {
+					t.Errorf("unexpected error: got = nil, want = %v", tt.wantErr)
+					return
+				}
+				if err.Error() != tt.wantErr.Error() {
+					t.Errorf("unexpected error: got = %v, want = %v", err, tt.wantErr)
+				}
+				return
+			}
+			if err != nil {
+				t.Errorf("unexpected error: got = %v, want = nil", err)
+			}
+			if !reflect.DeepEqual(rule.allowTypes, tt.wantTypes) {
+				t.Errorf("unexpected allowTypes: got = %v, want %v", rule.allowTypes, tt.wantTypes)
+			}
+		})
+	}
+}
diff --git a/rule/defer_test.go b/rule/defer_test.go
new file mode 100644
index 0000000..fc03c4c
--- /dev/null
+++ b/rule/defer_test.go
@@ -0,0 +1,137 @@
+package rule
+
+import (
+	"errors"
+	"reflect"
+	"testing"
+
+	"github.com/mgechev/revive/lint"
+)
+
+func TestDeferRule_Configure(t *testing.T) {
+	tests := []struct {
+		name      string
+		arguments lint.Arguments
+		wantErr   error
+		wantAllow map[string]bool
+	}{
+		{
+			name:      "no arguments",
+			arguments: lint.Arguments{},
+			wantErr:   nil,
+			wantAllow: map[string]bool{
+				"loop":             true,
+				"callchain":        true,
+				"methodcall":       true,
+				"return":           true,
+				"recover":          true,
+				"immediaterecover": true,
+			},
+		},
+		{
+			name: "valid arguments",
+			arguments: lint.Arguments{
+				[]any{
+					"loop",
+					"callChain",
+					"methodCall",
+					"return",
+					"recover",
+					"immediateRecover",
+				},
+			},
+			wantErr: nil,
+			wantAllow: map[string]bool{
+				"loop":             true,
+				"callchain":        true,
+				"methodcall":       true,
+				"return":           true,
+				"recover":          true,
+				"immediaterecover": true,
+			},
+		},
+		{
+			name: "valid lowercased arguments",
+			arguments: lint.Arguments{
+				[]any{
+					"loop",
+					"callchain",
+					"methodcall",
+					"return",
+					"recover",
+					"immediaterecover",
+				},
+			},
+			wantErr: nil,
+			wantAllow: map[string]bool{
+				"loop":             true,
+				"callchain":        true,
+				"methodcall":       true,
+				"return":           true,
+				"recover":          true,
+				"immediaterecover": true,
+			},
+		},
+		{
+			name: "valid kebab-cased arguments",
+			arguments: lint.Arguments{
+				[]any{
+					"loop",
+					"call-chain",
+					"method-call",
+					"return",
+					"recover",
+					"immediate-recover",
+				},
+			},
+			wantErr: nil,
+			wantAllow: map[string]bool{
+				"loop":             true,
+				"callchain":        true,
+				"methodcall":       true,
+				"return":           true,
+				"recover":          true,
+				"immediaterecover": true,
+			},
+		},
+		{
+			name: "invalid argument type",
+			arguments: lint.Arguments{
+				"invalid_argument",
+			},
+			wantErr: errors.New("invalid argument 'invalid_argument' for 'defer' rule. Expecting []string, got string"),
+		},
+		{
+			name: "invalid subcase type",
+			arguments: lint.Arguments{
+				[]any{123},
+			},
+			wantErr: errors.New("invalid argument '123' for 'defer' rule. Expecting string, got int"),
+		},
+	}
+
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			var rule DeferRule
+
+			err := rule.Configure(tt.arguments)
+
+			if tt.wantErr != nil {
+				if err == nil {
+					t.Errorf("unexpected error: got = nil, want = %v", tt.wantErr)
+					return
+				}
+				if err.Error() != tt.wantErr.Error() {
+					t.Errorf("unexpected error: got = %v, want = %v", err, tt.wantErr)
+				}
+				return
+			}
+			if err != nil {
+				t.Errorf("unexpected error: got = %v, want = nil", err)
+			}
+			if !reflect.DeepEqual(rule.allow, tt.wantAllow) {
+				t.Errorf("unexpected allow: got = %v, want %v", rule.allow, tt.wantAllow)
+			}
+		})
+	}
+}
diff --git a/rule/dot_imports_test.go b/rule/dot_imports_test.go
new file mode 100644
index 0000000..dd9e83f
--- /dev/null
+++ b/rule/dot_imports_test.go
@@ -0,0 +1,127 @@
+package rule
+
+import (
+	"errors"
+	"reflect"
+	"testing"
+
+	"github.com/mgechev/revive/lint"
+)
+
+func TestDotImportsRule_Configure(t *testing.T) {
+	tests := []struct {
+		name              string
+		arguments         lint.Arguments
+		wantErr           error
+		wantAllowPackages allowPackages
+	}{
+		{
+			name:              "no arguments",
+			arguments:         lint.Arguments{},
+			wantErr:           nil,
+			wantAllowPackages: allowPackages{},
+		},
+		{
+			name: "no allowedPackages key",
+			arguments: lint.Arguments{
+				map[string]any{
+					"invalid": "argument",
+				},
+			},
+			wantErr:           nil,
+			wantAllowPackages: allowPackages{},
+		},
+		{
+			name: "valid arguments",
+			arguments: lint.Arguments{
+				map[string]any{
+					"allowedPackages": []any{
+						"github.com/onsi/ginkgo/v2",
+					},
+				},
+			},
+			wantErr: nil,
+			wantAllowPackages: allowPackages{
+				`"github.com/onsi/ginkgo/v2"`: struct{}{},
+			},
+		},
+		{
+			name: "valid lowercased arguments",
+			arguments: lint.Arguments{
+				map[string]any{
+					"allowedpackages": []any{
+						"github.com/onsi/ginkgo/v2",
+					},
+				},
+			},
+			wantErr: nil,
+			wantAllowPackages: allowPackages{
+				`"github.com/onsi/ginkgo/v2"`: struct{}{},
+			},
+		},
+		{
+			name: "valid kebab-cased arguments",
+			arguments: lint.Arguments{
+				map[string]any{
+					"allowed-packages": []any{
+						"github.com/onsi/ginkgo/v2",
+					},
+				},
+			},
+			wantErr: nil,
+			wantAllowPackages: allowPackages{
+				`"github.com/onsi/ginkgo/v2"`: struct{}{},
+			},
+		},
+		{
+			name: "invalid argument type",
+			arguments: lint.Arguments{
+				"invalid_argument",
+			},
+			wantErr: errors.New("invalid argument to the dot-imports rule. Expecting a k,v map, got string"),
+		},
+		{
+			name: "invalid allowedPackages type",
+			arguments: lint.Arguments{
+				map[string]any{
+					"allowedPackages": "invalid",
+				},
+			},
+			wantErr: errors.New("invalid argument to the dot-imports rule, []string expected. Got 'invalid' (string)"),
+		},
+		{
+			name: "invalid allowedPackages value type",
+			arguments: lint.Arguments{
+				map[string]any{
+					"allowedPackages": []any{123},
+				},
+			},
+			wantErr: errors.New("invalid argument to the dot-imports rule, string expected. Got '123' (int)"),
+		},
+	}
+
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			var rule DotImportsRule
+
+			err := rule.Configure(tt.arguments)
+
+			if tt.wantErr != nil {
+				if err == nil {
+					t.Errorf("unexpected error: got = nil, want = %v", tt.wantErr)
+					return
+				}
+				if err.Error() != tt.wantErr.Error() {
+					t.Errorf("unexpected error: got = %v, want = %v", err, tt.wantErr)
+				}
+				return
+			}
+			if err != nil {
+				t.Errorf("unexpected error: got = %v, want = nil", err)
+			}
+			if !reflect.DeepEqual(rule.allowedPackages, tt.wantAllowPackages) {
+				t.Errorf("unexpected allowedPackages: got = %v, want %v", rule.allowedPackages, tt.wantAllowPackages)
+			}
+		})
+	}
+}
diff --git a/rule/early_return_test.go b/rule/early_return_test.go
new file mode 100644
index 0000000..2ad2208
--- /dev/null
+++ b/rule/early_return_test.go
@@ -0,0 +1,92 @@
+package rule
+
+import (
+	"testing"
+
+	"github.com/mgechev/revive/lint"
+)
+
+func TestEarlyReturn_Configure(t *testing.T) {
+	tests := []struct {
+		name              string
+		arguments         lint.Arguments
+		wantErr           error
+		wantPreserveScope bool
+		wantAllowJump     bool
+	}{
+		{
+			name:              "no arguments",
+			arguments:         lint.Arguments{},
+			wantErr:           nil,
+			wantPreserveScope: false,
+			wantAllowJump:     false,
+		},
+		{
+			name: "valid arguments",
+			arguments: lint.Arguments{
+				"preserveScope",
+				"allowJump",
+			},
+			wantErr:           nil,
+			wantPreserveScope: true,
+			wantAllowJump:     true,
+		},
+		{
+			name: "valid lowercased arguments",
+			arguments: lint.Arguments{
+				"preservescope",
+				"allowjump",
+			},
+			wantErr:           nil,
+			wantPreserveScope: true,
+			wantAllowJump:     true,
+		},
+		{
+			name: "valid kebab-cased arguments",
+			arguments: lint.Arguments{
+				"preserve-scope",
+				"allow-jump",
+			},
+			wantErr:           nil,
+			wantPreserveScope: true,
+			wantAllowJump:     true,
+		},
+		{
+			name: "invalid arguments",
+			arguments: lint.Arguments{
+				"unknown",
+			},
+			wantErr:           nil,
+			wantPreserveScope: false,
+			wantAllowJump:     false,
+		},
+	}
+
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			var rule EarlyReturnRule
+
+			err := rule.Configure(tt.arguments)
+
+			if tt.wantErr != nil {
+				if err == nil {
+					t.Errorf("unexpected error: got = nil, want = %v", tt.wantErr)
+					return
+				}
+				if err.Error() != tt.wantErr.Error() {
+					t.Errorf("unexpected error: got = %v, want = %v", err, tt.wantErr)
+				}
+				return
+			}
+			if err != nil {
+				t.Errorf("unexpected error: got = %v, want = nil", err)
+			}
+			if rule.preserveScope != tt.wantPreserveScope {
+				t.Errorf("unexpected preserveScope: got = %v, want = %v", rule.preserveScope, tt.wantPreserveScope)
+			}
+			if rule.allowJump != tt.wantAllowJump {
+				t.Errorf("unexpected allowJump: got = %v, want = %v", rule.allowJump, tt.wantAllowJump)
+			}
+		})
+	}
+}
diff --git a/rule/enforce_repeated_arg_type_style_test.go b/rule/enforce_repeated_arg_type_style_test.go
new file mode 100644
index 0000000..a6d2494
--- /dev/null
+++ b/rule/enforce_repeated_arg_type_style_test.go
@@ -0,0 +1,158 @@
+package rule
+
+import (
+	"errors"
+	"testing"
+
+	"github.com/mgechev/revive/lint"
+)
+
+func TestEnforceRepeatedArgTypeStyleRule_Configure(t *testing.T) {
+	tests := []struct {
+		name                string
+		arguments           lint.Arguments
+		wantErr             error
+		wantFuncArgStyle    enforceRepeatedArgTypeStyleType
+		wantFuncRetValStyle enforceRepeatedArgTypeStyleType
+	}{
+		{
+			name:                "no arguments",
+			arguments:           lint.Arguments{},
+			wantErr:             nil,
+			wantFuncArgStyle:    "any",
+			wantFuncRetValStyle: "any",
+		},
+		{
+			name: "valid arguments: short",
+			arguments: lint.Arguments{
+				"short",
+			},
+			wantErr:             nil,
+			wantFuncArgStyle:    "short",
+			wantFuncRetValStyle: "short",
+		},
+		{
+			name: "valid arguments",
+			arguments: lint.Arguments{
+				map[string]any{
+					"funcArgStyle":    "full",
+					"funcRetValStyle": "short",
+				},
+			},
+			wantErr:             nil,
+			wantFuncArgStyle:    "full",
+			wantFuncRetValStyle: "short",
+		},
+		{
+			name: "valid lowercased arguments",
+			arguments: lint.Arguments{
+				map[string]any{
+					"funcargstyle":    "full",
+					"funcretvalstyle": "short",
+				},
+			},
+			wantErr:             nil,
+			wantFuncArgStyle:    "full",
+			wantFuncRetValStyle: "short",
+		},
+		{
+			name: "valid kebab-cased arguments",
+			arguments: lint.Arguments{
+				map[string]any{
+					"func-arg-style":     "full",
+					"func-ret-val-style": "short",
+				},
+			},
+			wantErr:             nil,
+			wantFuncArgStyle:    "full",
+			wantFuncRetValStyle: "short",
+		},
+		{
+			name: "unrecognized key",
+			arguments: lint.Arguments{
+				map[string]any{
+					"unknownKey": "someValue",
+				},
+			},
+			wantErr: errors.New("invalid map key for 'enforce-repeated-arg-type-style' rule. Expecting 'funcArgStyle' or 'funcRetValStyle', got unknownKey"),
+		},
+		{
+			name: "invalid argument type",
+			arguments: lint.Arguments{
+				123,
+			},
+			wantErr: errors.New("invalid argument '123' for 'import-alias-naming' rule. Expecting string or map[string]string, got int"),
+		},
+		{
+			name: "invalid argument when string",
+			arguments: lint.Arguments{
+				"invalid_argument",
+			},
+			wantErr: errors.New("invalid argument to the enforce-repeated-arg-type-style rule: invalid repeated arg type style: invalid_argument (expecting one of [any short full])"),
+		},
+		{
+			name: "invalid funcArgStyle value",
+			arguments: lint.Arguments{
+				map[string]any{
+					"funcArgStyle": 123,
+				},
+			},
+			wantErr: errors.New("invalid map value type for 'enforce-repeated-arg-type-style' rule. Expecting string, got int"),
+		},
+		{
+			name: "invalid funcRetValStyle value",
+			arguments: lint.Arguments{
+				map[string]any{
+					"funcRetValStyle": 123,
+				},
+			},
+			wantErr: errors.New("invalid map value '123' for 'enforce-repeated-arg-type-style' rule. Expecting string, got int"),
+		},
+		{
+			name: "invalid funcArgStyle value: wrong string",
+			arguments: lint.Arguments{
+				map[string]any{
+					"funcArgStyle": "invalid",
+				},
+			},
+			wantErr: errors.New("invalid argument to the enforce-repeated-arg-type-style rule: invalid repeated arg type style: invalid (expecting one of [any short full])"),
+		},
+		{
+			name: "invalid funcRetValStyle value: wrong string",
+			arguments: lint.Arguments{
+				map[string]any{
+					"funcRetValStyle": "invalid",
+				},
+			},
+			wantErr: errors.New("invalid argument to the enforce-repeated-arg-type-style rule: invalid repeated arg type style: invalid (expecting one of [any short full])"),
+		},
+	}
+
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			var rule EnforceRepeatedArgTypeStyleRule
+
+			err := rule.Configure(tt.arguments)
+
+			if tt.wantErr != nil {
+				if err == nil {
+					t.Errorf("unexpected error: got = nil, want = %v", tt.wantErr)
+					return
+				}
+				if err.Error() != tt.wantErr.Error() {
+					t.Errorf("unexpected error: got = %v, want = %v", err, tt.wantErr)
+				}
+				return
+			}
+			if err != nil {
+				t.Errorf("unexpected error: got = %v, want = nil", err)
+			}
+			if rule.funcArgStyle != tt.wantFuncArgStyle {
+				t.Errorf("unexpected funcArgStyle: got = %v, want %v", rule.funcArgStyle, tt.wantFuncArgStyle)
+			}
+			if rule.funcRetValStyle != tt.wantFuncRetValStyle {
+				t.Errorf("unexpected funcRetValStyle: got = %v, want %v", rule.funcRetValStyle, tt.wantFuncRetValStyle)
+			}
+		})
+	}
+}
diff --git a/rule/exported_test.go b/rule/exported_test.go
new file mode 100644
index 0000000..cfbb355
--- /dev/null
+++ b/rule/exported_test.go
@@ -0,0 +1,178 @@
+package rule
+
+import (
+	"errors"
+	"testing"
+
+	"github.com/mgechev/revive/lint"
+)
+
+func TestExportedRule_Configure(t *testing.T) {
+	tests := []struct {
+		name               string
+		arguments          lint.Arguments
+		wantErr            error
+		wantDisabledChecks disabledChecks
+		wantStuttersMsg    string
+	}{
+		{
+			name:      "default configuration",
+			arguments: lint.Arguments{},
+			wantErr:   nil,
+			wantDisabledChecks: disabledChecks{
+				PrivateReceivers: true,
+				PublicInterfaces: true,
+			},
+			wantStuttersMsg: "stutters",
+		},
+		{
+			name: "valid arguments",
+			arguments: lint.Arguments{
+				"checkPrivateReceivers",
+				"disableStutteringCheck",
+				"checkPublicInterface",
+				"disableChecksOnConstants",
+				"disableChecksOnFunctions",
+				"disableChecksOnMethods",
+				"disableChecksOnTypes",
+				"disableChecksOnVariables",
+			},
+			wantErr: nil,
+			wantDisabledChecks: disabledChecks{
+				PrivateReceivers: false,
+				PublicInterfaces: false,
+				Const:            true,
+				Function:         true,
+				Method:           true,
+				Stuttering:       true,
+				Type:             true,
+				Var:              true,
+			},
+			wantStuttersMsg: "stutters",
+		},
+		{
+			name: "valid lowercased arguments",
+			arguments: lint.Arguments{
+				"checkprivatereceivers",
+				"disablestutteringcheck",
+				"checkpublicinterface",
+				"disablechecksonconstants",
+				"disablechecksonfunctions",
+				"disablechecksonmethods",
+				"disablechecksontypes",
+				"disablechecksonvariables",
+			},
+			wantErr: nil,
+			wantDisabledChecks: disabledChecks{
+				PrivateReceivers: false,
+				PublicInterfaces: false,
+				Const:            true,
+				Function:         true,
+				Method:           true,
+				Stuttering:       true,
+				Type:             true,
+				Var:              true,
+			},
+			wantStuttersMsg: "stutters",
+		},
+		{
+			name: "valid kebab-cased arguments",
+			arguments: lint.Arguments{
+				"check-private-receivers",
+				"disable-stuttering-check",
+				"check-public-interface",
+				"disable-checks-on-constants",
+				"disable-checks-on-functions",
+				"disable-checks-on-methods",
+				"disable-checks-on-types",
+				"disable-checks-on-variables",
+			},
+			wantErr: nil,
+			wantDisabledChecks: disabledChecks{
+				PrivateReceivers: false,
+				PublicInterfaces: false,
+				Const:            true,
+				Function:         true,
+				Method:           true,
+				Stuttering:       true,
+				Type:             true,
+				Var:              true,
+			},
+			wantStuttersMsg: "stutters",
+		},
+		{
+			name: "valid sayRepetitiveInsteadOfStutters",
+			arguments: lint.Arguments{
+				"sayRepetitiveInsteadOfStutters",
+			},
+			wantErr: nil,
+			wantDisabledChecks: disabledChecks{
+				PrivateReceivers: true,
+				PublicInterfaces: true,
+			},
+			wantStuttersMsg: "is repetitive",
+		},
+		{
+			name: "valid lowercased sayRepetitiveInsteadOfStutters",
+			arguments: lint.Arguments{
+				"sayrepetitiveinsteadofstutters",
+			},
+			wantErr: nil,
+			wantDisabledChecks: disabledChecks{
+				PrivateReceivers: true,
+				PublicInterfaces: true,
+			},
+			wantStuttersMsg: "is repetitive",
+		},
+		{
+			name: "valid kebab-cased sayRepetitiveInsteadOfStutters",
+			arguments: lint.Arguments{
+				"say-repetitive-instead-of-stutters",
+			},
+			wantErr: nil,
+			wantDisabledChecks: disabledChecks{
+				PrivateReceivers: true,
+				PublicInterfaces: true,
+			},
+			wantStuttersMsg: "is repetitive",
+		},
+		{
+			name:      "unknown configuration flag",
+			arguments: lint.Arguments{"unknownFlag"},
+			wantErr:   errors.New("unknown configuration flag unknownFlag for exported rule"),
+		},
+		{
+			name:      "invalid argument type",
+			arguments: lint.Arguments{123},
+			wantErr:   errors.New("invalid argument for the exported rule: expecting a string, got int"),
+		},
+	}
+
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			var rule ExportedRule
+
+			err := rule.Configure(tt.arguments)
+
+			if tt.wantErr != nil {
+				if err == nil {
+					t.Errorf("unexpected error: got = nil, want = %v", tt.wantErr)
+					return
+				}
+				if err.Error() != tt.wantErr.Error() {
+					t.Errorf("unexpected error: got = %v, want = %v", err, tt.wantErr)
+				}
+				return
+			}
+			if err != nil {
+				t.Errorf("unexpected error: got = %v, want = nil", err)
+			}
+			if rule.disabledChecks != tt.wantDisabledChecks {
+				t.Errorf("unexpected disabledChecks: got = %+v, want %+v", rule.disabledChecks, tt.wantDisabledChecks)
+			}
+			if rule.stuttersMsg != tt.wantStuttersMsg {
+				t.Errorf("unexpected stuttersMsg: got = %v, want %v", rule.stuttersMsg, tt.wantStuttersMsg)
+			}
+		})
+	}
+}
diff --git a/rule/file_length_limit_test.go b/rule/file_length_limit_test.go
new file mode 100644
index 0000000..69fb724
--- /dev/null
+++ b/rule/file_length_limit_test.go
@@ -0,0 +1,121 @@
+package rule
+
+import (
+	"errors"
+	"testing"
+
+	"github.com/mgechev/revive/lint"
+)
+
+func TestFileLengthLimitRule_Configure(t *testing.T) {
+	tests := []struct {
+		name               string
+		arguments          lint.Arguments
+		wantErr            error
+		wantMax            int
+		wantSkipComments   bool
+		wantSkipBlankLines bool
+	}{
+		{
+			name:               "no arguments",
+			arguments:          lint.Arguments{},
+			wantErr:            nil,
+			wantMax:            0,
+			wantSkipComments:   false,
+			wantSkipBlankLines: false,
+		},
+		{
+			name: "valid arguments",
+			arguments: lint.Arguments{map[string]any{
+				"max":            int64(100),
+				"skipComments":   true,
+				"skipBlankLines": true,
+			}},
+			wantErr:            nil,
+			wantMax:            100,
+			wantSkipComments:   true,
+			wantSkipBlankLines: true,
+		},
+		{
+			name: "valid lowercased arguments",
+			arguments: lint.Arguments{map[string]any{
+				"max":            int64(100),
+				"skipcomments":   true,
+				"skipblanklines": true,
+			}},
+			wantErr:            nil,
+			wantMax:            100,
+			wantSkipComments:   true,
+			wantSkipBlankLines: true,
+		},
+		{
+			name: "valid kebab-cased arguments",
+			arguments: lint.Arguments{map[string]any{
+				"max":              int64(100),
+				"skip-comments":    true,
+				"skip-blank-lines": true,
+			}},
+			wantErr:            nil,
+			wantMax:            100,
+			wantSkipComments:   true,
+			wantSkipBlankLines: true,
+		},
+		{
+			name:      "invalid argument",
+			arguments: lint.Arguments{123},
+			wantErr:   errors.New(`invalid argument to the "file-length-limit" rule. Expecting a k,v map, got int`),
+		},
+		{
+			name: "invalid max type",
+			arguments: lint.Arguments{map[string]any{
+				"max": "invalid",
+			}},
+			wantErr: errors.New(`invalid configuration value for max lines in "file-length-limit" rule; need positive int64 but got string`),
+		},
+		{
+			name: "invalid skipComments type",
+			arguments: lint.Arguments{map[string]any{
+				"skipComments": "invalid",
+			}},
+			wantErr: errors.New(`invalid configuration value for skip comments in "file-length-limit" rule; need bool but got string`),
+		},
+		{
+			name: "invalid skipBlankLines type",
+			arguments: lint.Arguments{map[string]any{
+				"skipBlankLines": "invalid",
+			}},
+			wantErr: errors.New(`invalid configuration value for skip blank lines in "file-length-limit" rule; need bool but got string`),
+		},
+	}
+
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			var rule FileLengthLimitRule
+
+			err := rule.Configure(tt.arguments)
+
+			if tt.wantErr != nil {
+				if err == nil {
+					t.Errorf("unexpected error: got = nil, want = %v", tt.wantErr)
+					return
+				}
+				if err.Error() != tt.wantErr.Error() {
+					t.Errorf("unexpected error: got = %v, want = %v", err, tt.wantErr)
+				}
+				return
+			}
+			if err != nil {
+				t.Errorf("unexpected error: got = %v, want = nil", err)
+			}
+			if rule.max != tt.wantMax {
+				t.Errorf("unexpected max: got = %v, want %v", rule.max, tt.wantMax)
+			}
+			if rule.skipComments != tt.wantSkipComments {
+				t.Errorf("unexpected skipComments: got = %v, want %v", rule.skipComments, tt.wantSkipComments)
+			}
+			if rule.skipBlankLines != tt.wantSkipBlankLines {
+				t.Errorf("unexpected skipBlankLines: got = %v, want %v", rule.skipBlankLines, tt.wantSkipBlankLines)
+			}
+		})
+	}
+}
diff --git a/rule/import_alias_naming_test.go b/rule/import_alias_naming_test.go
new file mode 100644
index 0000000..056b62f
--- /dev/null
+++ b/rule/import_alias_naming_test.go
@@ -0,0 +1,133 @@
+package rule
+
+import (
+	"errors"
+	"reflect"
+	"regexp"
+	"testing"
+
+	"github.com/mgechev/revive/lint"
+)
+
+func TestImportAliasNamingRule_Configure(t *testing.T) {
+	tests := []struct {
+		name           string
+		arguments      lint.Arguments
+		wantErr        error
+		wantAllowRegex *regexp.Regexp
+		wantDenyRegex  *regexp.Regexp
+	}{
+		{
+			name:           "no arguments",
+			arguments:      lint.Arguments{},
+			wantErr:        nil,
+			wantAllowRegex: regexp.MustCompile("^[a-z][a-z0-9]{0,}$"),
+			wantDenyRegex:  nil,
+		},
+		{
+			name:           "valid string argument",
+			arguments:      lint.Arguments{"^[a-z][a-z0-9]*$"},
+			wantErr:        nil,
+			wantAllowRegex: regexp.MustCompile("^[a-z][a-z0-9]*$"),
+			wantDenyRegex:  nil,
+		},
+		{
+			name: "valid map arguments",
+			arguments: lint.Arguments{map[string]any{
+				"allowRegex": "^[a-z][a-z0-9]*$",
+				"denyRegex":  "^v\\d+$",
+			}},
+			wantErr:        nil,
+			wantAllowRegex: regexp.MustCompile("^[a-z][a-z0-9]*$"),
+			wantDenyRegex:  regexp.MustCompile("^v\\d+$"),
+		},
+		{
+			name: "valid map lowercased arguments",
+			arguments: lint.Arguments{map[string]any{
+				"allowregex": "^[a-z][a-z0-9]*$",
+				"denyregex":  "^v\\d+$",
+			}},
+			wantErr:        nil,
+			wantAllowRegex: regexp.MustCompile("^[a-z][a-z0-9]*$"),
+			wantDenyRegex:  regexp.MustCompile("^v\\d+$"),
+		},
+		{
+			name: "valid map kebab-cased arguments",
+			arguments: lint.Arguments{map[string]any{
+				"allow-regex": "^[a-z][a-z0-9]*$",
+				"deny-regex":  "^v\\d+$",
+			}},
+			wantErr:        nil,
+			wantAllowRegex: regexp.MustCompile("^[a-z][a-z0-9]*$"),
+			wantDenyRegex:  regexp.MustCompile("^v\\d+$"),
+		},
+		{
+			name:      "invalid argument type",
+			arguments: lint.Arguments{123},
+			wantErr:   errors.New(`invalid argument '123' for 'import-alias-naming' rule. Expecting string or map[string]string, got int`),
+		},
+		{
+			name:      "invalid string argument regex",
+			arguments: lint.Arguments{"["},
+			wantErr:   errors.New("invalid argument to the import-alias-naming allowRegexp rule. Expecting \"[\" to be a valid regular expression, got: error parsing regexp: missing closing ]: `[`"),
+		},
+		{
+			name: "invalid map key",
+			arguments: lint.Arguments{
+				map[string]any{
+					"unknownKey": "value",
+				},
+			},
+			wantErr: errors.New(`invalid map key for 'import-alias-naming' rule. Expecting 'allowRegex' or 'denyRegex', got unknownKey`),
+		},
+		{
+			name: "invalid allowRegex type",
+			arguments: lint.Arguments{map[string]any{
+				"allowRegex": 123,
+			}},
+			wantErr: errors.New("invalid argument '123' for import-alias-naming allowRegexp rule. Expecting string, got int"),
+		},
+		{
+			name: "invalid denyRegex type",
+			arguments: lint.Arguments{map[string]any{
+				"denyRegex": 123,
+			}},
+			wantErr: errors.New("invalid argument '123' for import-alias-naming denyRegexp rule. Expecting string, got int"),
+		},
+		{
+			name: "invalid denyRegex regex",
+			arguments: lint.Arguments{map[string]any{
+				"denyRegex": "[",
+			}},
+			wantErr: errors.New(`invalid argument to the import-alias-naming denyRegexp rule. Expecting "[" to be a valid regular expression, got: error parsing regexp: missing closing ]: ` + "`[`"),
+		},
+	}
+
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			var rule ImportAliasNamingRule
+
+			err := rule.Configure(tt.arguments)
+
+			if tt.wantErr != nil {
+				if err == nil {
+					t.Errorf("unexpected error: got = nil, want = %v", tt.wantErr)
+					return
+				}
+				if err.Error() != tt.wantErr.Error() {
+					t.Errorf("unexpected error: got = %v, want = %v", err, tt.wantErr)
+				}
+				return
+			}
+			if err != nil {
+				t.Errorf("unexpected error: got = %v, want = nil", err)
+			}
+			if !reflect.DeepEqual(rule.allowRegexp, tt.wantAllowRegex) {
+				t.Errorf("unexpected allowRegex: got = %v, want %v", rule.allowRegexp, tt.wantAllowRegex)
+			}
+			if !reflect.DeepEqual(rule.denyRegexp, tt.wantDenyRegex) {
+				t.Errorf("unexpected denyRegexp: got = %v, want %v", rule.denyRegexp, tt.wantDenyRegex)
+			}
+		})
+	}
+}
diff --git a/rule/indent_error_flow_test.go b/rule/indent_error_flow_test.go
new file mode 100644
index 0000000..e1846d1
--- /dev/null
+++ b/rule/indent_error_flow_test.go
@@ -0,0 +1,80 @@
+package rule
+
+import (
+	"testing"
+
+	"github.com/mgechev/revive/lint"
+)
+
+func TestIndentErrorFlowRule_Configure(t *testing.T) {
+	tests := []struct {
+		name              string
+		arguments         lint.Arguments
+		wantErr           error
+		wantPreserveScope bool
+	}{
+		{
+			name:              "no arguments",
+			arguments:         lint.Arguments{},
+			wantErr:           nil,
+			wantPreserveScope: false,
+		},
+		{
+			name: "valid arguments",
+			arguments: lint.Arguments{
+				"preserveScope",
+			},
+			wantErr:           nil,
+			wantPreserveScope: true,
+		},
+		{
+			name: "valid lowercased arguments",
+			arguments: lint.Arguments{
+				"preservescope",
+			},
+			wantErr:           nil,
+			wantPreserveScope: true,
+		},
+		{
+			name: "valid kebab-cased arguments",
+			arguments: lint.Arguments{
+				"preserve-scope",
+			},
+			wantErr:           nil,
+			wantPreserveScope: true,
+		},
+		{
+			name: "invalid arguments",
+			arguments: lint.Arguments{
+				"unknown",
+			},
+			wantErr:           nil,
+			wantPreserveScope: false,
+		},
+	}
+
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			var rule IndentErrorFlowRule
+
+			err := rule.Configure(tt.arguments)
+
+			if tt.wantErr != nil {
+				if err == nil {
+					t.Errorf("unexpected error: got = nil, want = %v", tt.wantErr)
+					return
+				}
+				if err.Error() != tt.wantErr.Error() {
+					t.Errorf("unexpected error: got = %v, want = %v", err, tt.wantErr)
+				}
+				return
+			}
+			if err != nil {
+				t.Errorf("unexpected error: got = %v, want = nil", err)
+			}
+			if rule.preserveScope != tt.wantPreserveScope {
+				t.Errorf("unexpected preserveScope: got = %v, want = %v", rule.preserveScope, tt.wantPreserveScope)
+			}
+		})
+	}
+}
diff --git a/rule/receiver_naming_test.go b/rule/receiver_naming_test.go
new file mode 100644
index 0000000..5dfa0da
--- /dev/null
+++ b/rule/receiver_naming_test.go
@@ -0,0 +1,92 @@
+package rule
+
+import (
+	"errors"
+	"testing"
+
+	"github.com/mgechev/revive/lint"
+)
+
+func TestReceiverNamingRule_Configure(t *testing.T) {
+	tests := []struct {
+		name          string
+		arguments     lint.Arguments
+		wantErr       error
+		wantMaxLength int
+	}{
+		{
+			name:          "no arguments",
+			arguments:     lint.Arguments{},
+			wantErr:       nil,
+			wantMaxLength: -1,
+		},
+		{
+			name:      "invalid type",
+			arguments: lint.Arguments{123},
+			wantErr:   errors.New("unable to get arguments for rule receiver-naming. Expected object of key-value-pairs"),
+		},
+		{
+			name: "valid maxLength argument",
+			arguments: lint.Arguments{map[string]any{
+				"maxLength": int64(10),
+			}},
+			wantErr:       nil,
+			wantMaxLength: 10,
+		},
+		{
+			name: "valid lowercased argument",
+			arguments: lint.Arguments{map[string]any{
+				"maxlength": int64(10),
+			}},
+			wantErr:       nil,
+			wantMaxLength: 10,
+		},
+		{
+			name: "valid kebab-cased argument",
+			arguments: lint.Arguments{map[string]any{
+				"max-length": int64(10),
+			}},
+			wantErr:       nil,
+			wantMaxLength: 10,
+		},
+		{
+			name: "invalid maxLength type",
+			arguments: lint.Arguments{map[string]any{
+				"maxLength": "10",
+			}},
+			wantErr: errors.New("invalid value 10 for argument maxLength of rule receiver-naming, expected integer value got string"),
+		},
+		{
+			name: "unknown argument",
+			arguments: lint.Arguments{map[string]any{
+				"unknownKey": 10,
+			}},
+			wantErr: errors.New("unknown argument unknownKey for receiver-naming rule"),
+		},
+	}
+
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			var rule ReceiverNamingRule
+
+			err := rule.Configure(tt.arguments)
+
+			if tt.wantErr != nil {
+				if err == nil {
+					t.Errorf("unexpected error: got = nil, want = %v", tt.wantErr)
+					return
+				}
+				if err.Error() != tt.wantErr.Error() {
+					t.Errorf("unexpected error: got = %v, want = %v", err, tt.wantErr)
+				}
+				return
+			}
+			if err != nil {
+				t.Errorf("unexpected error: got = %v, want = nil", err)
+			}
+			if rule.receiverNameMaxLength != tt.wantMaxLength {
+				t.Errorf("unexpected maxLength: got = %v, want %v", rule.receiverNameMaxLength, tt.wantMaxLength)
+			}
+		})
+	}
+}
diff --git a/rule/superfluous_else_test.go b/rule/superfluous_else_test.go
new file mode 100644
index 0000000..8d6d8e1
--- /dev/null
+++ b/rule/superfluous_else_test.go
@@ -0,0 +1,80 @@
+package rule
+
+import (
+	"testing"
+
+	"github.com/mgechev/revive/lint"
+)
+
+func TestSuperfluousElseRule_Configure(t *testing.T) {
+	tests := []struct {
+		name              string
+		arguments         lint.Arguments
+		wantErr           error
+		wantPreserveScope bool
+	}{
+		{
+			name:              "no arguments",
+			arguments:         lint.Arguments{},
+			wantErr:           nil,
+			wantPreserveScope: false,
+		},
+		{
+			name: "valid arguments",
+			arguments: lint.Arguments{
+				"preserveScope",
+			},
+			wantErr:           nil,
+			wantPreserveScope: true,
+		},
+		{
+			name: "valid lowercased arguments",
+			arguments: lint.Arguments{
+				"preservescope",
+			},
+			wantErr:           nil,
+			wantPreserveScope: true,
+		},
+		{
+			name: "valid kebab-cased arguments",
+			arguments: lint.Arguments{
+				"preserve-scope",
+			},
+			wantErr:           nil,
+			wantPreserveScope: true,
+		},
+		{
+			name: "invalid arguments",
+			arguments: lint.Arguments{
+				"unknown",
+			},
+			wantErr:           nil,
+			wantPreserveScope: false,
+		},
+	}
+
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			var rule SuperfluousElseRule
+
+			err := rule.Configure(tt.arguments)
+
+			if tt.wantErr != nil {
+				if err == nil {
+					t.Errorf("unexpected error: got = nil, want = %v", tt.wantErr)
+					return
+				}
+				if err.Error() != tt.wantErr.Error() {
+					t.Errorf("unexpected error: got = %v, want = %v", err, tt.wantErr)
+				}
+				return
+			}
+			if err != nil {
+				t.Errorf("unexpected error: got = %v, want = nil", err)
+			}
+			if rule.preserveScope != tt.wantPreserveScope {
+				t.Errorf("unexpected preserveScope: got = %v, want = %v", rule.preserveScope, tt.wantPreserveScope)
+			}
+		})
+	}
+}
diff --git a/rule/unchecked_type_assertion_test.go b/rule/unchecked_type_assertion_test.go
new file mode 100644
index 0000000..6ed9a8f
--- /dev/null
+++ b/rule/unchecked_type_assertion_test.go
@@ -0,0 +1,92 @@
+package rule
+
+import (
+	"errors"
+	"testing"
+
+	"github.com/mgechev/revive/lint"
+)
+
+func TestUncheckedTypeAssertionRule_Configure(t *testing.T) {
+	tests := []struct {
+		name                             string
+		arguments                        lint.Arguments
+		wantErr                          error
+		wantAcceptIgnoredAssertionResult bool
+	}{
+		{
+			name:                             "no arguments",
+			arguments:                        lint.Arguments{},
+			wantErr:                          nil,
+			wantAcceptIgnoredAssertionResult: false,
+		},
+		{
+			name: "valid acceptIgnoredAssertionResult argument",
+			arguments: lint.Arguments{map[string]any{
+				"acceptIgnoredAssertionResult": true,
+			}},
+			wantErr:                          nil,
+			wantAcceptIgnoredAssertionResult: true,
+		},
+		{
+			name: "valid lowercased argument",
+			arguments: lint.Arguments{map[string]any{
+				"acceptignoredassertionresult": true,
+			}},
+			wantErr:                          nil,
+			wantAcceptIgnoredAssertionResult: true,
+		},
+		{
+			name: "valid kebab-cased argument",
+			arguments: lint.Arguments{map[string]any{
+				"accept-ignored-assertion-result": true,
+			}},
+			wantErr:                          nil,
+			wantAcceptIgnoredAssertionResult: true,
+		},
+		{
+			name:      "invalid type",
+			arguments: lint.Arguments{123},
+			wantErr:   errors.New("unable to get arguments. Expected object of key-value-pairs"),
+		},
+		{
+			name: "invalid acceptIgnoredAssertionResult type",
+			arguments: lint.Arguments{map[string]any{
+				"acceptIgnoredAssertionResult": "true",
+			}},
+			wantErr: errors.New("unable to parse argument 'acceptIgnoredAssertionResult'. Expected boolean"),
+		},
+		{
+			name: "unknown argument",
+			arguments: lint.Arguments{map[string]any{
+				"unknownKey": true,
+			}},
+			wantErr: errors.New("unknown argument: unknownKey"),
+		},
+	}
+
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			var rule UncheckedTypeAssertionRule
+
+			err := rule.Configure(tt.arguments)
+
+			if tt.wantErr != nil {
+				if err == nil {
+					t.Errorf("unexpected error: got = nil, want = %v", tt.wantErr)
+					return
+				}
+				if err.Error() != tt.wantErr.Error() {
+					t.Errorf("unexpected error: got = %v, want = %v", err, tt.wantErr)
+				}
+				return
+			}
+			if err != nil {
+				t.Errorf("unexpected error: got = %v, want = nil", err)
+			}
+			if rule.acceptIgnoredAssertionResult != tt.wantAcceptIgnoredAssertionResult {
+				t.Errorf("unexpected acceptIgnoredAssertionResult: got = %v, want %v", rule.acceptIgnoredAssertionResult, tt.wantAcceptIgnoredAssertionResult)
+			}
+		})
+	}
+}
diff --git a/rule/unused_param_test.go b/rule/unused_param_test.go
new file mode 100644
index 0000000..9258ff8
--- /dev/null
+++ b/rule/unused_param_test.go
@@ -0,0 +1,118 @@
+package rule
+
+import (
+	"errors"
+	"reflect"
+	"regexp"
+	"testing"
+
+	"github.com/mgechev/revive/lint"
+)
+
+func TestUnusedParamRule_Configure(t *testing.T) {
+	tests := []struct {
+		name           string
+		arguments      lint.Arguments
+		wantErr        error
+		wantAllowRegex *regexp.Regexp
+		wantFailureMsg string
+	}{
+		{
+			name:           "no arguments",
+			arguments:      lint.Arguments{},
+			wantErr:        nil,
+			wantAllowRegex: regexp.MustCompile("^_$"),
+			wantFailureMsg: "parameter '%s' seems to be unused, consider removing or renaming it as _",
+		},
+		{
+			name: "valid arguments",
+			arguments: lint.Arguments{
+				map[string]any{
+					"allowRegex": "^_",
+				},
+			},
+			wantErr:        nil,
+			wantAllowRegex: regexp.MustCompile("^_"),
+			wantFailureMsg: "parameter '%s' seems to be unused, consider removing or renaming it to match ^_",
+		},
+		{
+			name: "valid lowercased arguments",
+			arguments: lint.Arguments{
+				map[string]any{
+					"allowregex": "^_",
+				},
+			},
+			wantErr:        nil,
+			wantAllowRegex: regexp.MustCompile("^_"),
+			wantFailureMsg: "parameter '%s' seems to be unused, consider removing or renaming it to match ^_",
+		},
+		{
+			name: "valid kebab-cased arguments",
+			arguments: lint.Arguments{
+				map[string]any{
+					"allow-regex": "^_",
+				},
+			},
+			wantErr:        nil,
+			wantAllowRegex: regexp.MustCompile("^_"),
+			wantFailureMsg: "parameter '%s' seems to be unused, consider removing or renaming it to match ^_",
+		},
+		{
+			name: "missed allowRegex value",
+			arguments: lint.Arguments{
+				map[string]any{
+					"unknownKey": "123",
+				},
+			},
+			wantErr:        nil,
+			wantAllowRegex: regexp.MustCompile("^_$"),
+			wantFailureMsg: "parameter '%s' seems to be unused, consider removing or renaming it as _",
+		},
+		{
+			name: "invalid allowRegex: not a string",
+			arguments: lint.Arguments{
+				map[string]any{
+					"allowRegex": 123,
+				},
+			},
+			wantErr: errors.New("error configuring unused-parameter rule: allowRegex is not string but [int]"),
+		},
+		{
+			name: "invalid allowRegex: not a valid regex",
+			arguments: lint.Arguments{
+				map[string]any{
+					"allowRegex": "[",
+				},
+			},
+			wantErr: errors.New("error configuring unused-parameter rule: allowRegex is not valid regex [[]: error parsing regexp: missing closing ]: " + "`[`"),
+		},
+	}
+
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			var rule UnusedParamRule
+
+			err := rule.Configure(tt.arguments)
+
+			if tt.wantErr != nil {
+				if err == nil {
+					t.Errorf("unexpected error: got = nil, want = %v", tt.wantErr)
+					return
+				}
+				if err.Error() != tt.wantErr.Error() {
+					t.Errorf("unexpected error: got = %v, want = %v", err, tt.wantErr)
+				}
+				return
+			}
+			if err != nil {
+				t.Errorf("unexpected error: got = %v, want = nil", err)
+			}
+			if !reflect.DeepEqual(rule.allowRegex, tt.wantAllowRegex) {
+				t.Errorf("unexpected allowRegex: got = %v, want %v", rule.allowRegex, tt.wantAllowRegex)
+			}
+			if rule.failureMsg != tt.wantFailureMsg {
+				t.Errorf("unexpected failureMessage: got = %v, want %v", rule.failureMsg, tt.wantFailureMsg)
+			}
+		})
+	}
+}
diff --git a/rule/unused_receiver_test.go b/rule/unused_receiver_test.go
new file mode 100644
index 0000000..b021b1a
--- /dev/null
+++ b/rule/unused_receiver_test.go
@@ -0,0 +1,123 @@
+package rule
+
+import (
+	"errors"
+	"regexp"
+	"testing"
+
+	"github.com/mgechev/revive/lint"
+)
+
+func TestUnusedReceiverRule_Configure(t *testing.T) {
+	tests := []struct {
+		name           string
+		arguments      lint.Arguments
+		wantErr        error
+		wantRegex      *regexp.Regexp
+		wantFailureMsg string
+	}{
+		{
+			name:           "no arguments",
+			arguments:      lint.Arguments{},
+			wantErr:        nil,
+			wantRegex:      regexp.MustCompile("^_$"),
+			wantFailureMsg: "method receiver '%s' is not referenced in method's body, consider removing or renaming it as _",
+		},
+		{
+			name: "valid arguments",
+			arguments: lint.Arguments{
+				map[string]any{
+					"allowRegex": "^_",
+				},
+			},
+			wantErr:        nil,
+			wantRegex:      regexp.MustCompile("^_"),
+			wantFailureMsg: "method receiver '%s' is not referenced in method's body, consider removing or renaming it to match ^_",
+		},
+		{
+			name: "valid lowercased arguments",
+			arguments: lint.Arguments{
+				map[string]any{
+					"allowregex": "^_",
+				},
+			},
+			wantErr:        nil,
+			wantRegex:      regexp.MustCompile("^_"),
+			wantFailureMsg: "method receiver '%s' is not referenced in method's body, consider removing or renaming it to match ^_",
+		},
+		{
+			name: "valid kebab-cased arguments",
+			arguments: lint.Arguments{
+				map[string]any{
+					"allow-regex": "^_",
+				},
+			},
+			wantErr:        nil,
+			wantRegex:      regexp.MustCompile("^_"),
+			wantFailureMsg: "method receiver '%s' is not referenced in method's body, consider removing or renaming it to match ^_",
+		},
+		{
+			name: "argument is not a map",
+			arguments: lint.Arguments{
+				"invalid_argument",
+			},
+			wantErr:        nil,
+			wantFailureMsg: "method receiver '%s' is not referenced in method's body, consider removing or renaming it as _",
+		},
+		{
+			name: "missing allowRegex key",
+			arguments: lint.Arguments{
+				map[string]any{},
+			},
+			wantErr:        nil,
+			wantRegex:      allowBlankIdentifierRegex,
+			wantFailureMsg: "method receiver '%s' is not referenced in method's body, consider removing or renaming it as _",
+		},
+		{
+			name: "invalid allowRegex type",
+			arguments: lint.Arguments{
+				map[string]any{
+					"allowRegex": 123,
+				},
+			},
+			wantErr: errors.New("error configuring [unused-receiver] rule: allowRegex is not string but [int]"),
+		},
+		{
+			name: "invalid allowRegex value",
+			arguments: lint.Arguments{
+				map[string]any{
+					"allowRegex": "[invalid",
+				},
+			},
+			wantErr: errors.New("error configuring [unused-receiver] rule: allowRegex is not valid regex [[invalid]: error parsing regexp: missing closing ]: `[invalid`"),
+		},
+	}
+
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			var rule UnusedReceiverRule
+
+			err := rule.Configure(tt.arguments)
+
+			if tt.wantErr != nil {
+				if err == nil {
+					t.Errorf("unexpected error: got = nil, want = %v", tt.wantErr)
+					return
+				}
+				if err.Error() != tt.wantErr.Error() {
+					t.Errorf("unexpected error: got = %v, want = %v", err, tt.wantErr)
+				}
+				return
+			}
+			if err != nil {
+				t.Errorf("unexpected error: got = %v, want = nil", err)
+			}
+			if tt.wantRegex != nil && rule.allowRegex.String() != tt.wantRegex.String() {
+				t.Errorf("unexpected allowRegex: got = %v, want = %v", rule.allowRegex.String(), tt.wantRegex.String())
+			}
+			if tt.wantFailureMsg != rule.failureMsg {
+				t.Errorf("unexpected failureMsg: got = %v, want = %v", rule.failureMsg, tt.wantFailureMsg)
+			}
+		})
+	}
+}
diff --git a/rule/var_naming_test.go b/rule/var_naming_test.go
new file mode 100644
index 0000000..122c453
--- /dev/null
+++ b/rule/var_naming_test.go
@@ -0,0 +1,136 @@
+package rule
+
+import (
+	"errors"
+	"testing"
+
+	"github.com/mgechev/revive/lint"
+)
+
+func TestVarNamingRule_Configure(t *testing.T) {
+	tests := []struct {
+		name                      string
+		arguments                 lint.Arguments
+		wantErr                   error
+		wantAllowList             []string
+		wantBlockList             []string
+		wantAllowUpperCaseConst   bool
+		wantSkipPackageNameChecks bool
+	}{
+		{
+			name:                      "no arguments",
+			arguments:                 lint.Arguments{},
+			wantErr:                   nil,
+			wantAllowList:             nil,
+			wantBlockList:             nil,
+			wantAllowUpperCaseConst:   false,
+			wantSkipPackageNameChecks: false,
+		},
+		{
+			name: "valid arguments",
+			arguments: lint.Arguments{
+				[]any{"ID"},
+				[]any{"VM"},
+				[]any{map[string]any{
+					"upperCaseConst":        true,
+					"skipPackageNameChecks": true,
+				}},
+			},
+			wantErr:                   nil,
+			wantAllowList:             []string{"ID"},
+			wantBlockList:             []string{"VM"},
+			wantAllowUpperCaseConst:   true,
+			wantSkipPackageNameChecks: true,
+		},
+		{
+			name: "valid lowercased arguments",
+			arguments: lint.Arguments{
+				[]any{"ID"},
+				[]any{"VM"},
+				[]any{map[string]any{
+					"uppercaseconst":        true,
+					"skippackagenamechecks": true,
+				}},
+			},
+			wantErr:                   nil,
+			wantAllowList:             []string{"ID"},
+			wantBlockList:             []string{"VM"},
+			wantAllowUpperCaseConst:   true,
+			wantSkipPackageNameChecks: true,
+		},
+		{
+			name: "valid kebab-cased arguments",
+			arguments: lint.Arguments{
+				[]any{"ID"},
+				[]any{"VM"},
+				[]any{map[string]any{
+					"upper-case-const":         true,
+					"skip-package-name-checks": true,
+				}},
+			},
+			wantErr:                   nil,
+			wantAllowList:             []string{"ID"},
+			wantBlockList:             []string{"VM"},
+			wantAllowUpperCaseConst:   true,
+			wantSkipPackageNameChecks: true,
+		},
+		{
+			name:      "invalid allowlist type",
+			arguments: lint.Arguments{123},
+			wantErr:   errors.New("invalid argument to the var-naming rule. Expecting a allowlist of type slice with initialisms, got int"),
+		},
+		{
+			name:      "invalid allowlist value type",
+			arguments: lint.Arguments{[]any{123}},
+			wantErr:   errors.New("invalid 123 values of the var-naming rule. Expecting slice of strings but got element of type []interface {}"),
+		},
+		{
+			name:      "invalid blocklist type",
+			arguments: lint.Arguments{[]any{"ID"}, 123},
+			wantErr:   errors.New("invalid argument to the var-naming rule. Expecting a blocklist of type slice with initialisms, got int"),
+		},
+		{
+			name:      "invalid third argument type",
+			arguments: lint.Arguments{[]any{"ID"}, []any{"VM"}, 123},
+			wantErr:   errors.New("invalid third argument to the var-naming rule. Expecting a options of type slice, got int"),
+		},
+		{
+			name:      "invalid third argument slice size",
+			arguments: lint.Arguments{[]any{"ID"}, []any{"VM"}, []any{}},
+			wantErr:   errors.New("invalid third argument to the var-naming rule. Expecting a options of type slice, of len==1, but 0"),
+		},
+		{
+			name:      "invalid third argument first element type",
+			arguments: lint.Arguments{[]any{"ID"}, []any{"VM"}, []any{123}},
+			wantErr:   errors.New("invalid third argument to the var-naming rule. Expecting a options of type slice, of len==1, with map, but int"),
+		},
+	}
+
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			var rule VarNamingRule
+
+			err := rule.Configure(tt.arguments)
+
+			if tt.wantErr != nil {
+				if err == nil {
+					t.Errorf("unexpected error: got = nil, want = %v", tt.wantErr)
+					return
+				}
+				if err.Error() != tt.wantErr.Error() {
+					t.Errorf("unexpected error: got = %v, want = %v", err, tt.wantErr)
+				}
+				return
+			}
+			if err != nil {
+				t.Errorf("unexpected error: got = %v, want = nil", err)
+			}
+			if rule.allowUpperCaseConst != tt.wantAllowUpperCaseConst {
+				t.Errorf("unexpected allowUpperCaseConst: got = %v, want %v", rule.allowUpperCaseConst, tt.wantAllowUpperCaseConst)
+			}
+			if rule.skipPackageNameChecks != tt.wantSkipPackageNameChecks {
+				t.Errorf("unexpected skipPackageNameChecks: got = %v, want %v", rule.skipPackageNameChecks, tt.wantSkipPackageNameChecks)
+			}
+		})
+	}
+}
diff --git a/test/add_constant_test.go b/test/add_constant_test.go
index 1f20913..3f2105f 100644
--- a/test/add_constant_test.go
+++ b/test/add_constant_test.go
@@ -21,4 +21,13 @@ func TestAddConstantWithArguments(t *testing.T) {
 			"ignoreFuncs": "os\\.(CreateFile|WriteFile|Chmod|FindProcess),\\.Println,ignoredFunc,\\.Info",
 		}},
 	})
+	testRule(t, "add_constant", &rule.AddConstantRule{}, &lint.RuleConfig{
+		Arguments: []any{map[string]any{
+			"max-lit-count": "2",
+			"allow-strs":    `""`,
+			"allow-ints":    "0,1,2",
+			"allow-floats":  "0.0,1.0",
+			"ignore-funcs":  `os\.(CreateFile|WriteFile|Chmod|FindProcess),\.Println,ignoredFunc,\.Info`,
+		}},
+	})
 }
diff --git a/test/context_as_argument_test.go b/test/context_as_argument_test.go
index 67fa54d..7e37e6a 100644
--- a/test/context_as_argument_test.go
+++ b/test/context_as_argument_test.go
@@ -18,6 +18,12 @@ func TestContextAsArgument(t *testing.T) {
 				"allowTypesBefore": "AllowedBeforeType,AllowedBeforeStruct,*AllowedBeforePtrStruct,*testing.T",
 			},
 		},
-	},
-	)
+	})
+	testRule(t, "context_as_argument", &rule.ContextAsArgumentRule{}, &lint.RuleConfig{
+		Arguments: []any{
+			map[string]any{
+				"allow-types-before": "AllowedBeforeType,AllowedBeforeStruct,*AllowedBeforePtrStruct,*testing.T",
+			},
+		},
+	})
 }
diff --git a/test/defer_test.go b/test/defer_test.go
index 78ce52a..4155e3f 100644
--- a/test/defer_test.go
+++ b/test/defer_test.go
@@ -12,6 +12,9 @@ func TestDefer(t *testing.T) {
 }
 
 func TestDeferLoopDisabled(t *testing.T) {
+	testRule(t, "defer_loop_disabled", &rule.DeferRule{}, &lint.RuleConfig{
+		Arguments: []any{[]any{"return", "recover", "callChain", "methodCall"}},
+	})
 	testRule(t, "defer_loop_disabled", &rule.DeferRule{}, &lint.RuleConfig{
 		Arguments: []any{[]any{"return", "recover", "call-chain", "method-call"}},
 	})
diff --git a/test/dot_imports_test.go b/test/dot_imports_test.go
index 368021b..5dc9965 100644
--- a/test/dot_imports_test.go
+++ b/test/dot_imports_test.go
@@ -17,4 +17,9 @@ func TestDotImports(t *testing.T) {
 			"allowedPackages": []any{"errors", "context", "github.com/BurntSushi/toml"},
 		}},
 	})
+	testRule(t, "dot_imports", &rule.DotImportsRule{}, &lint.RuleConfig{
+		Arguments: []any{map[string]any{
+			"allowed-packages": []any{"errors", "context", "github.com/BurntSushi/toml"},
+		}},
+	})
 }
diff --git a/test/early_return_test.go b/test/early_return_test.go
index cc4ef66..07f41dd 100644
--- a/test/early_return_test.go
+++ b/test/early_return_test.go
@@ -3,13 +3,14 @@ package test
 import (
 	"testing"
 
-	"github.com/mgechev/revive/internal/ifelse"
 	"github.com/mgechev/revive/lint"
 	"github.com/mgechev/revive/rule"
 )
 
 func TestEarlyReturn(t *testing.T) {
 	testRule(t, "early_return", &rule.EarlyReturnRule{})
-	testRule(t, "early_return_scope", &rule.EarlyReturnRule{}, &lint.RuleConfig{Arguments: []any{ifelse.PreserveScope}})
-	testRule(t, "early_return_jump", &rule.EarlyReturnRule{}, &lint.RuleConfig{Arguments: []any{ifelse.AllowJump}})
+	testRule(t, "early_return_scope", &rule.EarlyReturnRule{}, &lint.RuleConfig{Arguments: []any{"preserveScope"}})
+	testRule(t, "early_return_scope", &rule.EarlyReturnRule{}, &lint.RuleConfig{Arguments: []any{"preserve-scope"}})
+	testRule(t, "early_return_jump", &rule.EarlyReturnRule{}, &lint.RuleConfig{Arguments: []any{"allowJump"}})
+	testRule(t, "early_return_jump", &rule.EarlyReturnRule{}, &lint.RuleConfig{Arguments: []any{"allow-jump"}})
 }
diff --git a/test/enforce_repeated_arg_type_style_test.go b/test/enforce_repeated_arg_type_style_test.go
index 153e3ae..f5b19be 100644
--- a/test/enforce_repeated_arg_type_style_test.go
+++ b/test/enforce_repeated_arg_type_style_test.go
@@ -26,6 +26,13 @@ func TestEnforceRepeatedArgTypeStyleShort(t *testing.T) {
 			},
 		},
 	})
+	testRule(t, "enforce_repeated_arg_type_style_short_args", &rule.EnforceRepeatedArgTypeStyleRule{}, &lint.RuleConfig{
+		Arguments: []any{
+			map[string]any{
+				"func-arg-style": `short`,
+			},
+		},
+	})
 	testRule(t, "enforce_repeated_arg_type_style_short_return", &rule.EnforceRepeatedArgTypeStyleRule{}, &lint.RuleConfig{
 		Arguments: []any{
 			map[string]any{
@@ -33,6 +40,13 @@ func TestEnforceRepeatedArgTypeStyleShort(t *testing.T) {
 			},
 		},
 	})
+	testRule(t, "enforce_repeated_arg_type_style_short_return", &rule.EnforceRepeatedArgTypeStyleRule{}, &lint.RuleConfig{
+		Arguments: []any{
+			map[string]any{
+				"func-ret-val-style": `short`,
+			},
+		},
+	})
 }
 
 func TestEnforceRepeatedArgTypeStyleFull(t *testing.T) {
diff --git a/test/exported_test.go b/test/exported_test.go
index a69b90e..6b35cbc 100644
--- a/test/exported_test.go
+++ b/test/exported_test.go
@@ -32,9 +32,12 @@ func TestCheckPublicInterfaceOption(t *testing.T) {
 }
 
 func TestCheckDisablingOnDeclarationTypes(t *testing.T) {
-	args := []any{"disableChecksOnConstants", "disableChecksOnFunctions", "disableChecksOnMethods", "disableChecksOnTypes", "disableChecksOnVariables"}
-
-	testRule(t, "exported_issue_1045", &rule.ExportedRule{}, &lint.RuleConfig{Arguments: args})
+	testRule(t, "exported_issue_1045", &rule.ExportedRule{}, &lint.RuleConfig{
+		Arguments: []any{"disableChecksOnConstants", "disableChecksOnFunctions", "disableChecksOnMethods", "disableChecksOnTypes", "disableChecksOnVariables"},
+	})
+	testRule(t, "exported_issue_1045", &rule.ExportedRule{}, &lint.RuleConfig{
+		Arguments: []any{"disable-checks-on-constants", "disable-checks-on-functions", "disable-checks-on-methods", "disable-checks-on-types", "disable-checks-on-variables"},
+	})
 }
 
 func TestCheckDirectiveComment(t *testing.T) {
diff --git a/test/file_length_limit_test.go b/test/file_length_limit_test.go
index 40002e2..1414b21 100644
--- a/test/file_length_limit_test.go
+++ b/test/file_length_limit_test.go
@@ -29,4 +29,7 @@ func TestFileLengthLimit(t *testing.T) {
 	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}},
 	})
+	testRule(t, "file_length_limit_4_skip_comments_skip_blank", &rule.FileLengthLimitRule{}, &lint.RuleConfig{
+		Arguments: []any{map[string]any{"max": int64(4), "skip-comments": true, "skip-blank-lines": true}},
+	})
 }
diff --git a/test/import_alias_naming_test.go b/test/import_alias_naming_test.go
index 5693608..a540f09 100644
--- a/test/import_alias_naming_test.go
+++ b/test/import_alias_naming_test.go
@@ -31,6 +31,14 @@ func TestImportAliasNaming_CustomConfigWithMultipleRules(t *testing.T) {
 			},
 		},
 	})
+	testRule(t, "import_alias_naming_custom_config_with_multiple_values", &rule.ImportAliasNamingRule{}, &lint.RuleConfig{
+		Arguments: []any{
+			map[string]any{
+				"allow-regex": `^[a-z][a-z0-9]*$`,
+				"deny-regex":  `^((v\d+)|(v\d+alpha\d+))$`,
+			},
+		},
+	})
 }
 
 func TestImportAliasNaming_CustomConfigWithOnlyDeny(t *testing.T) {
diff --git a/test/indent_error_flow_test.go b/test/indent_error_flow_test.go
new file mode 100644
index 0000000..2cc0c67
--- /dev/null
+++ b/test/indent_error_flow_test.go
@@ -0,0 +1,14 @@
+package test
+
+import (
+	"testing"
+
+	"github.com/mgechev/revive/lint"
+	"github.com/mgechev/revive/rule"
+)
+
+func TestIndentErrorFlow(t *testing.T) {
+	testRule(t, "indent_error_flow", &rule.IndentErrorFlowRule{})
+	testRule(t, "indent_error_flow_scope", &rule.IndentErrorFlowRule{}, &lint.RuleConfig{Arguments: []any{"preserveScope"}})
+	testRule(t, "indent_error_flow_scope", &rule.IndentErrorFlowRule{}, &lint.RuleConfig{Arguments: []any{"preserve-scope"}})
+}
diff --git a/test/receiver_naming_test.go b/test/receiver_naming_test.go
index 2c7b238..1809d93 100644
--- a/test/receiver_naming_test.go
+++ b/test/receiver_naming_test.go
@@ -16,9 +16,18 @@ func TestReceiverNamingTypeParams(t *testing.T) {
 }
 
 func TestReceiverNamingMaxLength(t *testing.T) {
-	args := []any{map[string]any{
-		"maxLength": int64(2),
-	}}
 	testRule(t, "receiver_naming_issue_1040", &rule.ReceiverNamingRule{},
-		&lint.RuleConfig{Arguments: args})
+		&lint.RuleConfig{
+			Arguments: []any{
+				map[string]any{"maxLength": int64(2)},
+			},
+		},
+	)
+	testRule(t, "receiver_naming_issue_1040", &rule.ReceiverNamingRule{},
+		&lint.RuleConfig{
+			Arguments: []any{
+				map[string]any{"max-length": int64(2)},
+			},
+		},
+	)
 }
diff --git a/test/superfluous_else_test.go b/test/superfluous_else_test.go
index 0ec40ac..e5ff7bb 100644
--- a/test/superfluous_else_test.go
+++ b/test/superfluous_else_test.go
@@ -3,12 +3,12 @@ package test
 import (
 	"testing"
 
-	"github.com/mgechev/revive/internal/ifelse"
 	"github.com/mgechev/revive/lint"
 	"github.com/mgechev/revive/rule"
 )
 
 func TestSuperfluousElse(t *testing.T) {
 	testRule(t, "superfluous_else", &rule.SuperfluousElseRule{})
-	testRule(t, "superfluous_else_scope", &rule.SuperfluousElseRule{}, &lint.RuleConfig{Arguments: []any{ifelse.PreserveScope}})
+	testRule(t, "superfluous_else_scope", &rule.SuperfluousElseRule{}, &lint.RuleConfig{Arguments: []any{"preserveScope"}})
+	testRule(t, "superfluous_else_scope", &rule.SuperfluousElseRule{}, &lint.RuleConfig{Arguments: []any{"preserve-scope"}})
 }
diff --git a/test/unchecked_type_assertion_test.go b/test/unchecked_type_assertion_test.go
index 8ae240f..1cee4da 100644
--- a/test/unchecked_type_assertion_test.go
+++ b/test/unchecked_type_assertion_test.go
@@ -12,9 +12,18 @@ func TestUncheckedDynamicCast(t *testing.T) {
 }
 
 func TestUncheckedDynamicCastWithAcceptIgnored(t *testing.T) {
-	args := []any{map[string]any{
-		"acceptIgnoredAssertionResult": true,
-	}}
-
-	testRule(t, "unchecked_type_assertion_accept_ignored", &rule.UncheckedTypeAssertionRule{}, &lint.RuleConfig{Arguments: args})
+	testRule(t, "unchecked_type_assertion_accept_ignored", &rule.UncheckedTypeAssertionRule{},
+		&lint.RuleConfig{
+			Arguments: []any{
+				map[string]any{"acceptIgnoredAssertionResult": true},
+			},
+		},
+	)
+	testRule(t, "unchecked_type_assertion_accept_ignored", &rule.UncheckedTypeAssertionRule{},
+		&lint.RuleConfig{
+			Arguments: []any{
+				map[string]any{"accept-ignored-assertion-result": true},
+			},
+		},
+	)
 }
diff --git a/test/unused_param_test.go b/test/unused_param_test.go
index c67316e..c69d84c 100644
--- a/test/unused_param_test.go
+++ b/test/unused_param_test.go
@@ -16,6 +16,9 @@ func TestUnusedParam(t *testing.T) {
 	testRule(t, "unused_param_custom_regex", &rule.UnusedParamRule{}, &lint.RuleConfig{Arguments: []any{
 		map[string]any{"allowRegex": "^xxx"},
 	}})
+	testRule(t, "unused_param_custom_regex", &rule.UnusedParamRule{}, &lint.RuleConfig{Arguments: []any{
+		map[string]any{"allow-regex": "^xxx"},
+	}})
 }
 
 func BenchmarkUnusedParam(b *testing.B) {
diff --git a/test/unused_receiver_test.go b/test/unused_receiver_test.go
index 3f1bc61..6177424 100644
--- a/test/unused_receiver_test.go
+++ b/test/unused_receiver_test.go
@@ -16,6 +16,9 @@ func TestUnusedReceiver(t *testing.T) {
 	testRule(t, "unused_receiver_custom_regex", &rule.UnusedReceiverRule{}, &lint.RuleConfig{Arguments: []any{
 		map[string]any{"allowRegex": "^xxx"},
 	}})
+	testRule(t, "unused_receiver_custom_regex", &rule.UnusedReceiverRule{}, &lint.RuleConfig{Arguments: []any{
+		map[string]any{"allow-regex": "^xxx"},
+	}})
 }
 
 func BenchmarkUnusedReceiver(b *testing.B) {
diff --git a/test/var_naming_test.go b/test/var_naming_test.go
index c523047..fd5d0a2 100644
--- a/test/var_naming_test.go
+++ b/test/var_naming_test.go
@@ -18,9 +18,15 @@ func TestVarNaming(t *testing.T) {
 	testRule(t, "var_naming_upper_case_const_true", &rule.VarNamingRule{}, &lint.RuleConfig{
 		Arguments: []any{[]any{}, []any{}, []any{map[string]any{"upperCaseConst": true}}},
 	})
+	testRule(t, "var_naming_upper_case_const_true", &rule.VarNamingRule{}, &lint.RuleConfig{
+		Arguments: []any{[]any{}, []any{}, []any{map[string]any{"upper-case-const": true}}},
+	})
 
 	testRule(t, "var_naming_skip_package_name_checks_false", &rule.VarNamingRule{}, &lint.RuleConfig{})
 	testRule(t, "var_naming_skip_package_name_checks_true", &rule.VarNamingRule{}, &lint.RuleConfig{
 		Arguments: []any{[]any{}, []any{}, []any{map[string]any{"skipPackageNameChecks": true}}},
 	})
+	testRule(t, "var_naming_skip_package_name_checks_true", &rule.VarNamingRule{}, &lint.RuleConfig{
+		Arguments: []any{[]any{}, []any{}, []any{map[string]any{"skip-package-name-checks": true}}},
+	})
 }
diff --git a/testdata/indent_error_flow.go b/testdata/indent_error_flow.go
new file mode 100644
index 0000000..64ea06e
--- /dev/null
+++ b/testdata/indent_error_flow.go
@@ -0,0 +1,49 @@
+// Test of return+else warning.
+
+// Package pkg ...
+package pkg
+
+import "log"
+
+func f(x int) bool {
+	if x > 0 {
+		return true
+	} else { // MATCH /if block ends with a return statement, so drop this else and outdent its block/
+		log.Printf("non-positive x: %d", x)
+	}
+	return false
+}
+
+func g(f func() bool) string {
+	if ok := f(); ok {
+		return "it's okay"
+	} else { // MATCH /if block ends with a return statement, so drop this else and outdent its block (move short variable declaration to its own line if necessary)/
+		return "it's NOT okay!"
+	}
+}
+
+func h(f func() bool, x int) string {
+	if err == author.ErrCourseNotFound {
+		return
+	} else if err == author.ErrCourseAccess {
+		// side effect
+	} else if err == author.AnotherError {
+		return "okay"
+	} else {
+		if ok := f(); ok {
+			return "it's okay"
+		} else { // MATCH /if block ends with a return statement, so drop this else and outdent its block (move short variable declaration to its own line if necessary)/
+			return "it's NOT okay!"
+		}
+	}
+}
+
+func i() string {
+	if err == author.ErrCourseNotFound {
+		return "not found"
+	} else if err == author.AnotherError {
+		return "something else"
+	} else { // MATCH /if block ends with a return statement, so drop this else and outdent its block/
+		return "okay"
+	}
+}
diff --git a/testdata/indent_error_flow_scope.go b/testdata/indent_error_flow_scope.go
new file mode 100644
index 0000000..1a9bc31
--- /dev/null
+++ b/testdata/indent_error_flow_scope.go
@@ -0,0 +1,15 @@
+package pkg
+
+func fn4() {
+	if cond {
+		var x = fn2()
+		fn3(x)
+		return
+	} else {
+		y := fn2()
+		fn3(y)
+	}
+	// Don't want to move the declaration of x here since it stays in scope afterward
+	y := fn2()
+	fn3(y)
+}