Repository: mgechev/revive
Pull request #1127 (2024-11-15 11:04:02)

refactor: extract getVersion function
The PR extracts some functionality to the `getVersion()` function and adds tests for it.


Patch:
diff --git a/RULES_DESCRIPTIONS.md b/RULES_DESCRIPTIONS.md
index f741224..d76f6b1 100644
--- a/RULES_DESCRIPTIONS.md
+++ b/RULES_DESCRIPTIONS.md
@@ -521,7 +521,7 @@ Example:
 ```
 
 ## 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.
+_Description_: enforces conventions on source file names. By default, the rule enforces filenames of the form `^[_A-Za-z0-9][_A-Za-z0-9-]*\.go$`: Optionally, the rule can be configured to enforce other forms.
 
 _Configuration_: (string) regular expression for source filenames.
 
@@ -529,7 +529,7 @@ Example:
 
 ```toml
 [rule.filename-format]
-    arguments=["^[_a-z][_a-z0-9]*.go$"]
+    arguments=["^[_a-z][_a-z0-9]*\\.go$"]
 ```
 
 ## flag-parameter
@@ -997,7 +997,7 @@ _Configuration_: N/A
 
 ## var-naming
 
-_Description_: This rule warns when [initialism](https://go.dev/wiki/CodeReviewComments#initialisms), [variable](https://go.dev/wiki/CodeReviewComments#variable-names) or [package](https://go.dev/wiki/CodeReviewComments#package-names) naming conventions are not followed.
+_Description_: This rule warns when [initialism](https://go.dev/wiki/CodeReviewComments#initialisms), [variable](https://go.dev/wiki/CodeReviewComments#variable-names) or [package](https://go.dev/wiki/CodeReviewComments#package-names) naming conventions are not followed. It ignores functions starting with `Example`, `Test`, `Benchmark`, and `Fuzz` in test files, preserving `golint` original behavior.
 
 _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)
diff --git a/cli/main.go b/cli/main.go
index 26ae3c3..c00f4b0 100644
--- a/cli/main.go
+++ b/cli/main.go
@@ -16,11 +16,18 @@ import (
 	"github.com/spf13/afero"
 )
 
+const (
+	defaultVersion = "dev"
+	defaultCommit  = "none"
+	defaultDate    = "unknown"
+	defaultBuilder = "unknown"
+)
+
 var (
-	version = "dev"
-	commit  = "none"
-	date    = "unknown"
-	builtBy = "unknown"
+	version = defaultVersion
+	commit  = defaultCommit
+	date    = defaultDate
+	builtBy = defaultBuilder
 	//AppFs is used to operations related with user config files
 	AppFs = afero.NewOsFs()
 )
@@ -35,6 +42,12 @@ func RunRevive(extraRules ...revivelib.ExtraRule) {
 	// move parsing flags outside of init() otherwise tests dont works properly
 	// more info: https://github.com/golang/go/issues/46869#issuecomment-865695953
 	initConfig()
+
+	if versionFlag {
+		fmt.Print(getVersion(builtBy, date, commit, version))
+		os.Exit(0)
+	}
+
 	conf, err := config.GetConfig(configPath)
 	if err != nil {
 		fail(err.Error())
@@ -160,35 +173,33 @@ func initConfig() {
 	flag.BoolVar(&setExitStatus, "set_exit_status", false, exitStatusUsage)
 	flag.IntVar(&maxOpenFiles, "max_open_files", 0, maxOpenFilesUsage)
 	flag.Parse()
+}
 
-	// Output build info (version, commit, date and builtBy)
-	if versionFlag {
-		var buildInfo string
-		if date != "unknown" && builtBy != "unknown" {
-			buildInfo = fmt.Sprintf("Built\t\t%s by %s\n", date, builtBy)
-		}
+// getVersion returns build info (version, commit, date and builtBy)
+func getVersion(builtBy, date, commit, version string) string {
+	var buildInfo string
+	if date != defaultDate && builtBy != defaultBuilder {
+		buildInfo = fmt.Sprintf("Built\t\t%s by %s\n", date, builtBy)
+	}
 
-		if commit != "none" {
-			buildInfo = fmt.Sprintf("Commit:\t\t%s\n%s", commit, buildInfo)
-		}
+	if commit != defaultCommit {
+		buildInfo = fmt.Sprintf("Commit:\t\t%s\n%s", commit, buildInfo)
+	}
 
-		if version == "dev" {
-			bi, ok := debug.ReadBuildInfo()
-			if ok {
-				version = bi.Main.Version
-				if strings.HasPrefix(version, "v") {
-					version = bi.Main.Version[1:]
-				}
-				if len(buildInfo) == 0 {
-					fmt.Printf("version %s\n", version)
-					os.Exit(0)
-				}
+	if version == defaultVersion {
+		bi, ok := debug.ReadBuildInfo()
+		if ok {
+			version = bi.Main.Version
+			if strings.HasPrefix(version, "v") {
+				version = strings.TrimLeft(bi.Main.Version, "v")
+			}
+			if len(buildInfo) == 0 {
+				return fmt.Sprintf("version %s\n", version)
 			}
 		}
-
-		fmt.Printf("Version:\t%s\n%s", version, buildInfo)
-		os.Exit(0)
 	}
+
+	return fmt.Sprintf("Version:\t%s\n%s", version, buildInfo)
 }
 
 func fileExist(path string) bool {
diff --git a/config/config.go b/config/config.go
index 16559f5..b8b2360 100644
--- a/config/config.go
+++ b/config/config.go
@@ -165,7 +165,7 @@ func parseConfig(path string, config *lint.Config) error {
 	if err != nil {
 		return errors.New("cannot read the config file")
 	}
-	_, err = toml.Decode(string(file), config)
+	err = toml.Unmarshal(file, config)
 	if err != nil {
 		return fmt.Errorf("cannot parse the config file: %v", err)
 	}
diff --git a/formatter/checkstyle.go b/formatter/checkstyle.go
index f45b63c..8fe85fa 100644
--- a/formatter/checkstyle.go
+++ b/formatter/checkstyle.go
@@ -45,7 +45,7 @@ func (*Checkstyle) Format(failures <-chan lint.Failure, config lint.Config) (str
 		}
 		fn := failure.GetFilename()
 		if issues[fn] == nil {
-			issues[fn] = make([]issue, 0)
+			issues[fn] = []issue{}
 		}
 		issues[fn] = append(issues[fn], iss)
 	}
diff --git a/formatter/default.go b/formatter/default.go
index 2d5a044..7af4aad 100644
--- a/formatter/default.go
+++ b/formatter/default.go
@@ -26,3 +26,7 @@ func (*Default) Format(failures <-chan lint.Failure, _ lint.Config) (string, err
 	}
 	return buf.String(), nil
 }
+
+func ruleDescriptionURL(ruleName string) string {
+	return "https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#" + ruleName
+}
diff --git a/formatter/friendly.go b/formatter/friendly.go
index 5ff329a..bbbbcb6 100644
--- a/formatter/friendly.go
+++ b/formatter/friendly.go
@@ -58,8 +58,7 @@ func (f *Friendly) Format(failures <-chan lint.Failure, config lint.Config) (str
 func (f *Friendly) printFriendlyFailure(w io.Writer, failure lint.Failure, severity lint.Severity) {
 	f.printHeaderRow(w, failure, severity)
 	f.printFilePosition(w, failure)
-	fmt.Fprintln(w)
-	fmt.Fprintln(w)
+	fmt.Fprintf(w, "\n\n")
 }
 
 func (f *Friendly) printHeaderRow(w io.Writer, failure lint.Failure, severity lint.Severity) {
@@ -67,7 +66,7 @@ func (f *Friendly) printHeaderRow(w io.Writer, failure lint.Failure, severity li
 	if severity == lint.SeverityError {
 		emoji = getErrorEmoji()
 	}
-	fmt.Fprint(w, f.table([][]string{{emoji, "https://revive.run/r#" + failure.RuleName, color.GreenString(failure.Failure)}}))
+	fmt.Fprint(w, f.table([][]string{{emoji, ruleDescriptionURL(failure.RuleName), color.GreenString(failure.Failure)}}))
 }
 
 func (*Friendly) printFilePosition(w io.Writer, failure lint.Failure) {
@@ -98,13 +97,11 @@ func (*Friendly) printSummary(w io.Writer, errors, warnings int) {
 	}
 	str := fmt.Sprintf("%d %s (%d %s, %d %s)", errors+warnings, problemsLabel, errors, errorsLabel, warnings, warningsLabel)
 	if errors > 0 {
-		fmt.Fprintf(w, "%s %s\n", emoji, color.RedString(str))
-		fmt.Fprintln(w)
+		fmt.Fprintf(w, "%s %s\n\n", emoji, color.RedString(str))
 		return
 	}
 	if warnings > 0 {
-		fmt.Fprintf(w, "%s %s\n", emoji, color.YellowString(str))
-		fmt.Fprintln(w)
+		fmt.Fprintf(w, "%s %s\n\n", emoji, color.YellowString(str))
 		return
 	}
 }
diff --git a/formatter/plain.go b/formatter/plain.go
index 09ebf6c..c972432 100644
--- a/formatter/plain.go
+++ b/formatter/plain.go
@@ -22,7 +22,7 @@ func (*Plain) Name() string {
 func (*Plain) Format(failures <-chan lint.Failure, _ lint.Config) (string, error) {
 	var buf bytes.Buffer
 	for failure := range failures {
-		fmt.Fprintf(&buf, "%v: %s %s\n", failure.Position.Start, failure.Failure, "https://revive.run/r#"+failure.RuleName)
+		fmt.Fprintf(&buf, "%v: %s %s\n", failure.Position.Start, failure.Failure, ruleDescriptionURL(failure.RuleName))
 	}
 	return buf.String(), nil
 }
diff --git a/formatter/sarif.go b/formatter/sarif.go
index c42da73..72da160 100644
--- a/formatter/sarif.go
+++ b/formatter/sarif.go
@@ -20,7 +20,7 @@ func (*Sarif) Name() string {
 	return "sarif"
 }
 
-const reviveSite = "https://revive.run"
+const reviveSite = "https://github.com/mgechev/revive"
 
 // Format formats the failures gotten from the lint.
 func (*Sarif) Format(failures <-chan lint.Failure, cfg lint.Config) (string, error) {
diff --git a/formatter/stylish.go b/formatter/stylish.go
index 828228c..957a605 100644
--- a/formatter/stylish.go
+++ b/formatter/stylish.go
@@ -22,11 +22,12 @@ func (*Stylish) Name() string {
 
 func formatFailure(failure lint.Failure, severity lint.Severity) []string {
 	fString := color.CyanString(failure.Failure)
-	fName := color.RedString("https://revive.run/r#" + failure.RuleName)
+	fURL := ruleDescriptionURL(failure.RuleName)
+	fName := color.RedString(fURL)
 	lineColumn := failure.Position
 	pos := fmt.Sprintf("(%d, %d)", lineColumn.Start.Line, lineColumn.Start.Column)
 	if severity == lint.SeverityWarning {
-		fName = color.YellowString("https://revive.run/r#" + failure.RuleName)
+		fName = color.YellowString(fURL)
 	}
 	return []string{failure.GetFilename(), pos, fName, fString}
 }
@@ -50,7 +51,7 @@ func (*Stylish) Format(failures <-chan lint.Failure, config lint.Config) (string
 		ps = "problem"
 	}
 
-	fileReport := make(map[string][][]string)
+	fileReport := map[string][][]string{}
 
 	for _, row := range result {
 		if _, ok := fileReport[row[0]]; !ok {
diff --git a/lint/file.go b/lint/file.go
index e34f8b7..c695d63 100644
--- a/lint/file.go
+++ b/lint/file.go
@@ -140,10 +140,10 @@ const (
 var re = regexp.MustCompile(directiveRE)
 
 func (f *File) disabledIntervals(rules []Rule, mustSpecifyDisableReason bool, failures chan Failure) disabledIntervalsMap {
-	enabledDisabledRulesMap := make(map[string][]enableDisableConfig)
+	enabledDisabledRulesMap := map[string][]enableDisableConfig{}
 
 	getEnabledDisabledIntervals := func() disabledIntervalsMap {
-		result := make(disabledIntervalsMap)
+		result := disabledIntervalsMap{}
 
 		for ruleName, disabledArr := range enabledDisabledRulesMap {
 			ruleResult := []DisabledInterval{}
diff --git a/lint/linter.go b/lint/linter.go
index b777f92..10f5cac 100644
--- a/lint/linter.go
+++ b/lint/linter.go
@@ -63,7 +63,7 @@ var (
 func (l *Linter) Lint(packages [][]string, ruleSet []Rule, config Config) (<-chan Failure, error) {
 	failures := make(chan Failure)
 
-	perModVersions := make(map[string]*goversion.Version)
+	perModVersions := map[string]*goversion.Version{}
 	perPkgVersions := make([]*goversion.Version, len(packages))
 	for n, files := range packages {
 		if len(files) == 0 {
diff --git a/lint/package.go b/lint/package.go
index 4a633f3..873f8a0 100644
--- a/lint/package.go
+++ b/lint/package.go
@@ -99,10 +99,10 @@ func (p *Package) TypeCheck() error {
 		Importer: importer.Default(),
 	}
 	info := &types.Info{
-		Types:  make(map[ast.Expr]types.TypeAndValue),
-		Defs:   make(map[*ast.Ident]types.Object),
-		Uses:   make(map[*ast.Ident]types.Object),
-		Scopes: make(map[ast.Node]*types.Scope),
+		Types:  map[ast.Expr]types.TypeAndValue{},
+		Defs:   map[*ast.Ident]types.Object{},
+		Uses:   map[*ast.Ident]types.Object{},
+		Scopes: map[ast.Node]*types.Scope{},
 	}
 	var anyFile *File
 	var astFiles []*ast.File
@@ -162,7 +162,7 @@ func (w *walker) Visit(n ast.Node) ast.Visitor {
 }
 
 func (p *Package) scanSortable() {
-	p.sortable = make(map[string]bool)
+	p.sortable = map[string]bool{}
 
 	// bitfield for which methods exist on each type.
 	const (
@@ -171,7 +171,7 @@ func (p *Package) scanSortable() {
 		bfSwap
 	)
 	nmap := map[string]int{"Len": bfLen, "Less": bfLess, "Swap": bfSwap}
-	has := make(map[string]int)
+	has := map[string]int{}
 	for _, f := range p.files {
 		ast.Walk(&walker{nmap, has}, f.AST)
 	}
diff --git a/revive.toml b/revive.toml
index 304909d..91733df 100644
--- a/revive.toml
+++ b/revive.toml
@@ -14,6 +14,10 @@ warningCode = 1
 [rule.dot-imports]
 [rule.empty-block]
 [rule.empty-lines]
+[rule.enforce-map-style]
+    arguments = ["literal"]
+[rule.enforce-slice-style]
+    arguments = ["literal"]
 [rule.error-naming]
 [rule.error-return]
 [rule.error-strings]
@@ -21,7 +25,7 @@ warningCode = 1
 [rule.exported]
 [rule.filename-format]
     # Override the default pattern to forbid .go files with uppercase letters and dashes.
-    arguments=["^[_a-z][_a-z0-9]*.go$"]
+    arguments=["^[_a-z][_a-z0-9]*\\.go$"]
 [rule.increment-decrement]
 [rule.indent-error-flow]
 [rule.line-length-limit]
diff --git a/rule/add_constant.go b/rule/add_constant.go
index 399382c..bf822e7 100644
--- a/rule/add_constant.go
+++ b/rule/add_constant.go
@@ -52,11 +52,11 @@ func (r *AddConstantRule) Apply(file *lint.File, arguments lint.Arguments) []lin
 
 	w := &lintAddConstantRule{
 		onFailure:       onFailure,
-		strLits:         make(map[string]int),
+		strLits:         map[string]int{},
 		strLitLimit:     r.strLitLimit,
 		allowList:       r.allowList,
 		ignoreFunctions: r.ignoreFunctions,
-		structTags:      make(map[*ast.BasicLit]struct{}),
+		structTags:      map[*ast.BasicLit]struct{}{},
 	}
 
 	ast.Walk(w, file.AST)
@@ -202,65 +202,64 @@ func (w *lintAddConstantRule) isStructTag(n *ast.BasicLit) bool {
 }
 
 func (r *AddConstantRule) configure(arguments lint.Arguments) {
-	if r.allowList == nil {
-		r.strLitLimit = defaultStrLitLimit
-		r.allowList = newAllowList()
-		if len(arguments) > 0 {
-			args, ok := arguments[0].(map[string]any)
+	r.strLitLimit = defaultStrLitLimit
+	r.allowList = newAllowList()
+	if len(arguments) == 0 {
+		return
+	}
+	args, ok := arguments[0].(map[string]any)
+	if !ok {
+		panic(fmt.Sprintf("Invalid argument to the add-constant rule, expecting a k,v map. Got %T", arguments[0]))
+	}
+	for k, v := range args {
+		kind := ""
+		switch k {
+		case "allowFloats":
+			kind = kindFLOAT
+			fallthrough
+		case "allowInts":
+			if kind == "" {
+				kind = kindINT
+			}
+			fallthrough
+		case "allowStrs":
+			if kind == "" {
+				kind = kindSTRING
+			}
+			list, ok := v.(string)
+			if !ok {
+				panic(fmt.Sprintf("Invalid argument to the add-constant rule, string expected. Got '%v' (%T)", v, v))
+			}
+			r.allowList.add(kind, list)
+		case "maxLitCount":
+			sl, ok := v.(string)
 			if !ok {
-				panic(fmt.Sprintf("Invalid argument to the add-constant rule. Expecting a k,v map, got %T", arguments[0]))
+				panic(fmt.Sprintf("Invalid argument to the add-constant rule, expecting string representation of an integer. Got '%v' (%T)", v, v))
 			}
-			for k, v := range args {
-				kind := ""
-				switch k {
-				case "allowFloats":
-					kind = kindFLOAT
-					fallthrough
-				case "allowInts":
-					if kind == "" {
-						kind = kindINT
-					}
-					fallthrough
-				case "allowStrs":
-					if kind == "" {
-						kind = kindSTRING
-					}
-					list, ok := v.(string)
-					if !ok {
-						panic(fmt.Sprintf("Invalid argument to the add-constant rule, string expected. Got '%v' (%T)", v, v))
-					}
-					r.allowList.add(kind, list)
-				case "maxLitCount":
-					sl, ok := v.(string)
-					if !ok {
-						panic(fmt.Sprintf("Invalid argument to the add-constant rule, expecting string representation of an integer. Got '%v' (%T)", v, v))
-					}
-
-					limit, err := strconv.Atoi(sl)
-					if err != nil {
-						panic(fmt.Sprintf("Invalid argument to the add-constant rule, expecting string representation of an integer. Got '%v'", v))
-					}
-					r.strLitLimit = limit
-				case "ignoreFuncs":
-					excludes, ok := v.(string)
-					if !ok {
-						panic(fmt.Sprintf("Invalid argument to the ignoreFuncs parameter of add-constant rule, string expected. Got '%v' (%T)", v, v))
-					}
-
-					for _, exclude := range strings.Split(excludes, ",") {
-						exclude = strings.Trim(exclude, " ")
-						if exclude == "" {
-							panic("Invalid argument to the ignoreFuncs parameter of add-constant rule, expected regular expression must not be empty.")
-						}
-
-						exp, err := regexp.Compile(exclude)
-						if err != nil {
-							panic(fmt.Sprintf("Invalid argument to the ignoreFuncs parameter of add-constant rule: regexp %q does not compile: %v", exclude, err))
-						}
-
-						r.ignoreFunctions = append(r.ignoreFunctions, exp)
-					}
+
+			limit, err := strconv.Atoi(sl)
+			if err != nil {
+				panic(fmt.Sprintf("Invalid argument to the add-constant rule, expecting string representation of an integer. Got '%v'", v))
+			}
+			r.strLitLimit = limit
+		case "ignoreFuncs":
+			excludes, ok := v.(string)
+			if !ok {
+				panic(fmt.Sprintf("Invalid argument to the ignoreFuncs parameter of add-constant rule, string expected. Got '%v' (%T)", v, v))
+			}
+
+			for _, exclude := range strings.Split(excludes, ",") {
+				exclude = strings.Trim(exclude, " ")
+				if exclude == "" {
+					panic("Invalid argument to the ignoreFuncs parameter of add-constant rule, expected regular expression must not be empty.")
 				}
+
+				exp, err := regexp.Compile(exclude)
+				if err != nil {
+					panic(fmt.Sprintf("Invalid argument to the ignoreFuncs parameter of add-constant rule: regexp %q does not compile: %v", exclude, err))
+				}
+
+				r.ignoreFunctions = append(r.ignoreFunctions, exp)
 			}
 		}
 	}
diff --git a/rule/confusing_naming.go b/rule/confusing_naming.go
index 32f6dd8..6a99793 100644
--- a/rule/confusing_naming.go
+++ b/rule/confusing_naming.go
@@ -35,7 +35,7 @@ func (ps *packages) methodNames(lp *lint.Package) pkgMethods {
 		}
 	}
 
-	pkgm := pkgMethods{pkg: lp, methods: make(map[string]map[string]*referenceMethod), mu: &sync.Mutex{}}
+	pkgm := pkgMethods{pkg: lp, methods: map[string]map[string]*referenceMethod{}, mu: &sync.Mutex{}}
 	ps.pkgs = append(ps.pkgs, pkgm)
 
 	return pkgm
diff --git a/rule/dot_imports.go b/rule/dot_imports.go
index f6c7fbc..03deb4a 100644
--- a/rule/dot_imports.go
+++ b/rule/dot_imports.go
@@ -42,7 +42,7 @@ func (*DotImportsRule) Name() string {
 }
 
 func (r *DotImportsRule) configure(arguments lint.Arguments) {
-	r.allowedPackages = make(allowPackages)
+	r.allowedPackages = allowPackages{}
 	if len(arguments) == 0 {
 		return
 	}
diff --git a/rule/empty_block.go b/rule/empty_block.go
index 25a052a..ba5652a 100644
--- a/rule/empty_block.go
+++ b/rule/empty_block.go
@@ -17,7 +17,7 @@ func (*EmptyBlockRule) Apply(file *lint.File, _ lint.Arguments) []lint.Failure {
 		failures = append(failures, failure)
 	}
 
-	w := lintEmptyBlock{make(map[*ast.BlockStmt]bool), onFailure}
+	w := lintEmptyBlock{map[*ast.BlockStmt]bool{}, onFailure}
 	ast.Walk(w, file.AST)
 	return failures
 }
diff --git a/rule/exported.go b/rule/exported.go
index 7ee27b3..b9f1e46 100644
--- a/rule/exported.go
+++ b/rule/exported.go
@@ -112,7 +112,7 @@ func (r *ExportedRule) Apply(file *lint.File, args lint.Arguments) []lint.Failur
 		onFailure: func(failure lint.Failure) {
 			failures = append(failures, failure)
 		},
-		genDeclMissingComments: make(map[*ast.GenDecl]bool),
+		genDeclMissingComments: map[*ast.GenDecl]bool{},
 		stuttersMsg:            r.stuttersMsg,
 		disabledChecks:         r.disabledChecks,
 	}
diff --git a/rule/filename_format.go b/rule/filename_format.go
index 9d80478..dbdb42d 100644
--- a/rule/filename_format.go
+++ b/rule/filename_format.go
@@ -53,7 +53,7 @@ func (*FilenameFormatRule) Name() string {
 	return "filename-format"
 }
 
-var defaultFormat = regexp.MustCompile("^[_A-Za-z0-9][_A-Za-z0-9-]*.go$")
+var defaultFormat = regexp.MustCompile(`^[_A-Za-z0-9][_A-Za-z0-9-]*\.go$`)
 
 func (r *FilenameFormatRule) configure(arguments lint.Arguments) {
 	argsCount := len(arguments)


Test patch:
diff --git a/cli/main_test.go b/cli/main_test.go
index fd3a5ba..6e1eee4 100644
--- a/cli/main_test.go
+++ b/cli/main_test.go
@@ -86,3 +86,44 @@ func TestXDGConfigDirNoFile(t *testing.T) {
 		t.Errorf("got %q, wanted %q", got, want)
 	}
 }
+
+func TestGetVersion(t *testing.T) {
+	tests := []struct {
+		name    string
+		version string
+		commit  string
+		date    string
+		builtBy string
+		want    string
+	}{
+		{
+			name:    "Development version",
+			version: defaultVersion,
+			commit:  defaultCommit,
+			date:    defaultDate,
+			builtBy: defaultBuilder,
+			want:    "version \n",
+		},
+		{
+			name:    "Release version",
+			version: "v1.5.0-12-g7ee4500-dev",
+			commit:  "7ee4500e125e2d1b12653b2c8e140fec380919b4",
+			date:    "2024-11-15 10:52 UTC",
+			builtBy: "builder",
+			want: `Version:	v1.5.0-12-g7ee4500-dev
+Commit:		7ee4500e125e2d1b12653b2c8e140fec380919b4
+Built		2024-11-15 10:52 UTC by builder
+`,
+		},
+	}
+
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			got := getVersion(tt.builtBy, tt.date, tt.commit, tt.version)
+
+			if got != tt.want {
+				t.Errorf("getVersion() = %q, want %q", got, tt.want)
+			}
+		})
+	}
+}
diff --git a/formatter/formatter_test.go b/formatter/formatter_test.go
index e0d4e9d..89f46b1 100644
--- a/formatter/formatter_test.go
+++ b/formatter/formatter_test.go
@@ -50,7 +50,7 @@ func TestFormatter(t *testing.T) {
 		{
 			formatter: &formatter.Friendly{},
 			want: `
-⚠  https://revive.run/r#rule  test failure  
+⚠  https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#rule  test failure  
   test.go:2:5
 
 ⚠ 1 problem (0 errors, 1 warning)
@@ -69,7 +69,7 @@ Warnings:
 		},
 		{
 			formatter: &formatter.Plain{},
-			want:      `test.go:2:5: test failure https://revive.run/r#rule`,
+			want:      `test.go:2:5: test failure https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#rule`,
 		},
 		{
 			formatter: &formatter.Sarif{},
@@ -100,7 +100,7 @@ Warnings:
       ],
       "tool": {
         "driver": {
-          "informationUri": "https://revive.run",
+          "informationUri": "https://github.com/mgechev/revive",
           "name": "revive"
         }
       }
@@ -114,7 +114,7 @@ Warnings:
 			formatter: &formatter.Stylish{},
 			want: `
 test.go
-  (2, 5)  https://revive.run/r#rule  test failure  
+  (2, 5)  https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#rule  test failure  
 
 
  ✖ 1 problem (0 errors) (1 warnings)
diff --git a/revivelib/core_test.go b/revivelib/core_test.go
index 10670c7..60ed94b 100644
--- a/revivelib/core_test.go
+++ b/revivelib/core_test.go
@@ -54,11 +54,11 @@ func TestReviveFormat(t *testing.T) {
 	}
 
 	errorMsgs := []string{
-		"(91, 3)  https://revive.run/r#unreachable-code  unreachable code after this statement",
-		"(98, 3)  https://revive.run/r#unreachable-code  unreachable code after this statement",
-		"(15, 2)  https://revive.run/r#if-return         redundant if ...; err != nil check, just return error instead.",
-		"(88, 3)  https://revive.run/r#if-return         redundant if ...; err != nil check, just return error instead.",
-		"(95, 3)  https://revive.run/r#if-return         redundant if ...; err != nil check, just return error instead.",
+		"(91, 3)  https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#unreachable-code  unreachable code after this statement",
+		"(98, 3)  https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#unreachable-code  unreachable code after this statement",
+		"(15, 2)  https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#if-return         redundant if ...; err != nil check, just return error instead.",
+		"(88, 3)  https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#if-return         redundant if ...; err != nil check, just return error instead.",
+		"(95, 3)  https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#if-return         redundant if ...; err != nil check, just return error instead.",
 	}
 	for _, errorMsg := range errorMsgs {
 		if !strings.Contains(failures, errorMsg) {
diff --git a/test/add_constant_test.go b/test/add_constant_test.go
index 6480ed5..1f20913 100644
--- a/test/add_constant_test.go
+++ b/test/add_constant_test.go
@@ -7,16 +7,18 @@ import (
 	"github.com/mgechev/revive/rule"
 )
 
-func TestAddConstant(t *testing.T) {
-	args := []any{map[string]any{
-		"maxLitCount": "2",
-		"allowStrs":   "\"\"",
-		"allowInts":   "0,1,2",
-		"allowFloats": "0.0,1.0",
-		"ignoreFuncs": "os\\.(CreateFile|WriteFile|Chmod|FindProcess),\\.Println,ignoredFunc,\\.Info",
-	}}
+func TestAddConstantWithDefaultArguments(t *testing.T) {
+	testRule(t, "add_constant_default", &rule.AddConstantRule{}, &lint.RuleConfig{})
+}
 
+func TestAddConstantWithArguments(t *testing.T) {
 	testRule(t, "add_constant", &rule.AddConstantRule{}, &lint.RuleConfig{
-		Arguments: args,
+		Arguments: []any{map[string]any{
+			"maxLitCount": "2",
+			"allowStrs":   "\"\"",
+			"allowInts":   "0,1,2",
+			"allowFloats": "0.0,1.0",
+			"ignoreFuncs": "os\\.(CreateFile|WriteFile|Chmod|FindProcess),\\.Println,ignoredFunc,\\.Info",
+		}},
 	})
 }
diff --git a/test/utils_test.go b/test/utils_test.go
index c6270db..3687d37 100644
--- a/test/utils_test.go
+++ b/test/utils_test.go
@@ -159,7 +159,7 @@ func parseInstructions(t *testing.T, filename string, src []byte) []instruction
 			}
 			if line == "OK" && ins == nil {
 				// so our return value will be non-nil
-				ins = make([]instruction, 0)
+				ins = []instruction{}
 				continue
 			}
 			switch extractDataMode(line) {
diff --git a/testdata/add_constant_default.go b/testdata/add_constant_default.go
new file mode 100644
index 0000000..2580f39
--- /dev/null
+++ b/testdata/add_constant_default.go
@@ -0,0 +1,12 @@
+package fixtures
+
+func foo() {
+	a = "ignore"
+	b = "ignore"
+
+	c = "match"
+	d = "match"
+	e = "match" // MATCH /string literal "match" appears, at least, 3 times, create a named constant for it/
+
+	f = 5 // MATCH /avoid magic numbers like '5', create a named constant for it/
+}
diff --git "a/testdata/filenam\320\265_with_non_ascii_char.go" "b/testdata/filenam\320\265_with_non_ascii_char.go"
index d6cf886..d70d2bf 100644
--- "a/testdata/filenam\320\265_with_non_ascii_char.go"
+++ "b/testdata/filenam\320\265_with_non_ascii_char.go"
@@ -1,3 +1,3 @@
 package main
 
-// MATCH:1 /Filename filenamе_with_non_ascii_char.go is not of the format ^[_A-Za-z0-9][_A-Za-z0-9-]*.go$. Non ASCII character е (U+0435) found./
+// MATCH:1 /Filename filenamе_with_non_ascii_char.go is not of the format ^[_A-Za-z0-9][_A-Za-z0-9-]*\.go$. Non ASCII character е (U+0435) found./