Repository: mgechev/revive
Pull request #655 (2022-03-22 02:51:53)

Separating lib from cli
This PR corresponds to issue #654. 

It extends Revive usage as a library to more than just cli usage. Clients can now use parts of the revive workflow (Lint, Format) and pass configuration to the flow themselves, instead of loading from a toml file.

After this PR users can use the library with:

```go
        // removed error handling for clarity
	conf, _ := config.GetConfig("./defaults.toml")

	revive, _ := revivelib.New(
		conf,  // config
		true,  // set exit code
		2048,  // max open files
		[]string{"./tests"},  // exclude
		revivelib.NewExtraRule(&mockRule{}, lint.RuleConfig{}),  // extra rules
	)

	// Now they can lint
     	failuresChan, _ := revive.Lint("../testdata/if-return.go")

	// Get the []lint.failure list
        failureList := revive.GetLintFailures(failuresChan)

        // Or just get the formatted output and exit code
	failures, exitCode, _ := revive.Format("stylish", failuresChan)
```

heynemann: @chavacava can you review this PR? If you want I can change revivelib to just lib. I just felt like doing:

```go
revive, err := revivelib.New(...)
```
Feels nicer than `lib.New(...)`.
chavacava: Hi @heynemann, I did not forget your PR, I'll try to check it this we. Thanks!
heynemann: Sorry for the initial low quality of this PR. It feels much better now. If you feel like it is up to par, just merge it :D

Patch:
diff --git a/README.md b/README.md
index 9f94268..58c9086 100644
--- a/README.md
+++ b/README.md
@@ -588,7 +588,57 @@ import (
 )
 
 func main() {
-	cli.RunRevive(cli.NewExtraRule(&myRule{}, lint.RuleConfig{}))
+	cli.RunRevive(revivelib.NewExtraRule(&myRule{}, lint.RuleConfig{}))
+}
+
+type myRule struct{}
+
+func (f myRule) Name() string {
+	return "myRule"
+}
+
+func (f myRule) Apply(*lint.File, lint.Arguments) []lint.Failure { ... }
+```
+
+You can still go further and use `revive` without its cli, as part of your library, or your own cli:
+
+```go
+package mylib
+
+import (
+	"github.com/mgechev/revive/cli"
+	"github.com/mgechev/revive/revivelib"
+	"github.com/mgechev/revive/lint"
+)
+
+// Error checking removed for clarity
+func LintMyFile(file string) {
+	conf, _:= config.GetConfig("../defaults.toml")
+
+	revive, _ := revivelib.New(
+		conf,  // Configuration file
+		true,  // Set exit status
+		2048,  // Max open files
+
+        // Then add as many extra rules as you need
+		revivelib.NewExtraRule(&myRule{}, lint.RuleConfig{}),
+	)
+
+	failuresChan, err := revive.Lint(
+ 		revivelib.Include(file),
+ 		revivelib.Exclude("./fixtures"),
+ 		// You can use as many revivelib.Include or revivelib.Exclude as required
+ 	)
+  	if err != nil {
+  	 	panic("Shouldn't have failed: " + err.Error)
+  	}
+
+  	// Now let's return the formatted errors
+	failures, exitCode, _ := revive.Format("stylish", failuresChan)
+
+  	// failures is the string with all formatted lint error messages
+  	// exit code is 0 if no errors, 1 if errors (unless config options change it)
+  	// ... do something with them
 }
 
 type myRule struct{}
@@ -691,6 +741,12 @@ REVIVE_FORCE_COLOR=1 revive -formatter friendly ./... | tee revive.log
 :---: |:---: |:---: |:---: |:---: |:---: |
 [ridvansumset](https://github.com/ridvansumset) |[Jarema](https://github.com/Jarema) |[vkrol](https://github.com/vkrol) |[haya14busa](https://github.com/haya14busa) |[sina-devel](https://github.com/sina-devel) |[techknowlogick](https://github.com/techknowlogick) |
 
+
+[<img alt="heynemann" src="https://avatars.githubusercontent.com/u/60965?v=4&s=117" width="117">](https://github.com/heynemann) | | | | | |
+|:---: |:---: |:---: |:---: |:---: |:---: |
+[heynemann](https://github.com/heynemann) | | | | | |
+
+
 ## License
 
 MIT
diff --git a/cli/main.go b/cli/main.go
index c1d8cba..a07e31b 100644
--- a/cli/main.go
+++ b/cli/main.go
@@ -3,17 +3,14 @@ package cli
 import (
 	"flag"
 	"fmt"
-	"io/ioutil"
 	"os"
 	"path/filepath"
 	"runtime/debug"
 	"strings"
 
 	"github.com/fatih/color"
-	"github.com/mgechev/dots"
 	"github.com/mgechev/revive/config"
-	"github.com/mgechev/revive/lint"
-	"github.com/mgechev/revive/logging"
+	"github.com/mgechev/revive/revivelib"
 	"github.com/mitchellh/go-homedir"
 )
 
@@ -29,109 +26,44 @@ func fail(err string) {
 	os.Exit(1)
 }
 
-// ExtraRule configures a new rule to be used with revive.
-type ExtraRule struct {
-	Rule          lint.Rule
-	DefaultConfig lint.RuleConfig
-}
-
-// NewExtraRule returns a configured extra rule
-func NewExtraRule(rule lint.Rule, defaultConfig lint.RuleConfig) ExtraRule {
-	return ExtraRule{
-		Rule:          rule,
-		DefaultConfig: defaultConfig,
-	}
-}
-
 // RunRevive runs the CLI for revive.
-func RunRevive(extraRules ...ExtraRule) {
-	log, err := logging.GetLogger()
+func RunRevive(extraRules ...revivelib.ExtraRule) {
+	conf, err := config.GetConfig(configPath)
 	if err != nil {
 		fail(err.Error())
 	}
 
-	formatter, err := config.GetFormatter(formatterName)
+	revive, err := revivelib.New(
+		conf,
+		setExitStatus,
+		maxOpenFiles,
+		extraRules...,
+	)
 	if err != nil {
 		fail(err.Error())
 	}
 
-	conf, err := config.GetConfig(configPath)
-	if err != nil {
-		fail(err.Error())
-	}
+	files := flag.Args()
+	packages := []*revivelib.LintPattern{}
 
-	if setExitStatus {
-		conf.ErrorCode = 1
-		conf.WarningCode = 1
+	for _, file := range files {
+		packages = append(packages, revivelib.Include(file))
 	}
 
-	extraRuleInstances := make([]lint.Rule, len(extraRules))
-	for i, extraRule := range extraRules {
-		extraRuleInstances[i] = extraRule.Rule
-
-		ruleName := extraRule.Rule.Name()
-		_, isRuleAlreadyConfigured := conf.Rules[ruleName]
-		if !isRuleAlreadyConfigured {
-			conf.Rules[ruleName] = extraRule.DefaultConfig
-		}
+	for _, file := range excludePatterns {
+		packages = append(packages, revivelib.Exclude(file))
 	}
 
-	lintingRules, err := config.GetLintingRules(conf, extraRuleInstances)
+	failures, err := revive.Lint(packages...)
 	if err != nil {
 		fail(err.Error())
 	}
 
-	log.Println("Config loaded")
-
-	if len(excludePaths) == 0 { // if no excludes were set in the command line
-		excludePaths = conf.Exclude // use those from the configuration
-	}
-
-	packages, err := getPackages(excludePaths)
+	output, exitCode, err := revive.Format(formatterName, failures)
 	if err != nil {
 		fail(err.Error())
 	}
-	revive := lint.New(func(file string) ([]byte, error) {
-		return ioutil.ReadFile(file)
-	}, maxOpenFiles)
-
-	failures, err := revive.Lint(packages, lintingRules, *conf)
-	if err != nil {
-		fail(err.Error())
-	}
-
-	formatChan := make(chan lint.Failure)
-	exitChan := make(chan bool)
-
-	var output string
-	go (func() {
-		output, err = formatter.Format(formatChan, *conf)
-		if err != nil {
-			fail(err.Error())
-		}
-		exitChan <- true
-	})()
-
-	exitCode := 0
-	for f := range failures {
-		if f.Confidence < conf.Confidence {
-			continue
-		}
-		if exitCode == 0 {
-			exitCode = conf.WarningCode
-		}
-		if c, ok := conf.Rules[f.RuleName]; ok && c.Severity == lint.SeverityError {
-			exitCode = conf.ErrorCode
-		}
-		if c, ok := conf.Directives[f.RuleName]; ok && c.Severity == lint.SeverityError {
-			exitCode = conf.ErrorCode
-		}
-
-		formatChan <- f
-	}
 
-	close(formatChan)
-	<-exitChan
 	if output != "" {
 		fmt.Println(output)
 	}
@@ -139,50 +71,13 @@ func RunRevive(extraRules ...ExtraRule) {
 	os.Exit(exitCode)
 }
 
-func normalizeSplit(strs []string) []string {
-	res := []string{}
-	for _, s := range strs {
-		t := strings.Trim(s, " \t")
-		if len(t) > 0 {
-			res = append(res, t)
-		}
-	}
-	return res
-}
-
-func getPackages(excludePaths arrayFlags) ([][]string, error) {
-	globs := normalizeSplit(flag.Args())
-	if len(globs) == 0 {
-		globs = append(globs, ".")
-	}
-
-	packages, err := dots.ResolvePackages(globs, normalizeSplit(excludePaths))
-	if err != nil {
-		return nil, err
-	}
-
-	return packages, nil
-}
-
-type arrayFlags []string
-
-func (i *arrayFlags) String() string {
-	return strings.Join([]string(*i), " ")
-}
-
-func (i *arrayFlags) Set(value string) error {
-	*i = append(*i, value)
-	return nil
-}
-
 var (
-	configPath    string
-	excludePaths  arrayFlags
-	formatterName string
-	help          bool
-	versionFlag   bool
-	setExitStatus bool
-	maxOpenFiles  int
+	configPath      string
+	excludePatterns revivelib.ArrayFlags
+	formatterName   string
+	versionFlag     bool
+	setExitStatus   bool
+	maxOpenFiles    int
 )
 
 var originalUsage = flag.Usage
@@ -243,7 +138,7 @@ func init() {
 	defaultConfigPath := buildDefaultConfigPath()
 
 	flag.StringVar(&configPath, "config", defaultConfigPath, configUsage)
-	flag.Var(&excludePaths, "exclude", excludeUsage)
+	flag.Var(&excludePatterns, "exclude", excludeUsage)
 	flag.StringVar(&formatterName, "formatter", "", formatterUsage)
 	flag.BoolVar(&versionFlag, "version", false, versionUsage)
 	flag.BoolVar(&setExitStatus, "set_exit_status", false, exitStatusUsage)
diff --git a/revivelib/core.go b/revivelib/core.go
new file mode 100644
index 0000000..0562973
--- /dev/null
+++ b/revivelib/core.go
@@ -0,0 +1,204 @@
+package revivelib
+
+import (
+	"io/ioutil"
+	"log"
+	"strings"
+
+	"github.com/mgechev/dots"
+	"github.com/mgechev/revive/config"
+	"github.com/mgechev/revive/lint"
+	"github.com/mgechev/revive/logging"
+	"github.com/pkg/errors"
+)
+
+// Revive is responsible for running linters and formatters
+// and returning a set of results.
+type Revive struct {
+	config       *lint.Config
+	lintingRules []lint.Rule
+	logger       *log.Logger
+	maxOpenFiles int
+}
+
+// New creates a new instance of Revive lint runner.
+func New(
+	conf *lint.Config,
+	setExitStatus bool,
+	maxOpenFiles int,
+	extraRules ...ExtraRule,
+) (*Revive, error) {
+	log, err := logging.GetLogger()
+	if err != nil {
+		return nil, errors.Wrap(err, "initializing revive - getting logger")
+	}
+
+	if setExitStatus {
+		conf.ErrorCode = 1
+		conf.WarningCode = 1
+	}
+
+	extraRuleInstances := make([]lint.Rule, len(extraRules))
+	for i, extraRule := range extraRules {
+		extraRuleInstances[i] = extraRule.Rule
+
+		ruleName := extraRule.Rule.Name()
+
+		_, isRuleAlreadyConfigured := conf.Rules[ruleName]
+		if !isRuleAlreadyConfigured {
+			conf.Rules[ruleName] = extraRule.DefaultConfig
+		}
+	}
+
+	lintingRules, err := config.GetLintingRules(conf, extraRuleInstances)
+	if err != nil {
+		return nil, errors.Wrap(err, "initializing revive - gettint lint rules")
+	}
+
+	log.Println("Config loaded")
+
+	return &Revive{
+		logger:       log,
+		config:       conf,
+		lintingRules: lintingRules,
+		maxOpenFiles: maxOpenFiles,
+	}, nil
+}
+
+// Lint the included patterns, skipping excluded ones
+func (r *Revive) Lint(patterns ...*LintPattern) (<-chan lint.Failure, error) {
+	includePatterns := []string{}
+	excludePatterns := []string{}
+
+	for _, lintpkg := range patterns {
+		if lintpkg.IsExclude() {
+			excludePatterns = append(excludePatterns, lintpkg.GetPattern())
+		} else {
+			includePatterns = append(includePatterns, lintpkg.GetPattern())
+		}
+	}
+
+	if len(excludePatterns) == 0 { // if no excludes were set
+		excludePatterns = r.config.Exclude // use those from the configuration
+	}
+
+	packages, err := getPackages(includePatterns, excludePatterns)
+	if err != nil {
+		return nil, errors.Wrap(err, "linting - getting packages")
+	}
+
+	revive := lint.New(func(file string) ([]byte, error) {
+		contents, err := ioutil.ReadFile(file)
+
+		if err != nil {
+			return nil, errors.Wrap(err, "reading file "+file)
+		}
+
+		return contents, nil
+	}, r.maxOpenFiles)
+
+	failures, err := revive.Lint(packages, r.lintingRules, *r.config)
+	if err != nil {
+		return nil, errors.Wrap(err, "linting - retrieving failures channel")
+	}
+
+	return failures, nil
+}
+
+// Format gets the output for a given failures channel from Lint.
+func (r *Revive) Format(
+	formatterName string,
+	failuresChan <-chan lint.Failure,
+) (string, int, error) {
+	conf := r.config
+	formatChan := make(chan lint.Failure)
+	exitChan := make(chan bool)
+
+	formatter, err := config.GetFormatter(formatterName)
+	if err != nil {
+		return "", 0, errors.Wrap(err, "formatting - getting formatter")
+	}
+
+	var (
+		output    string
+		formatErr error
+	)
+
+	go func() {
+		output, formatErr = formatter.Format(formatChan, *conf)
+
+		exitChan <- true
+	}()
+
+	exitCode := 0
+
+	for failure := range failuresChan {
+		if failure.Confidence < conf.Confidence {
+			continue
+		}
+
+		if exitCode == 0 {
+			exitCode = conf.WarningCode
+		}
+
+		if c, ok := conf.Rules[failure.RuleName]; ok && c.Severity == lint.SeverityError {
+			exitCode = conf.ErrorCode
+		}
+
+		if c, ok := conf.Directives[failure.RuleName]; ok && c.Severity == lint.SeverityError {
+			exitCode = conf.ErrorCode
+		}
+
+		formatChan <- failure
+	}
+
+	close(formatChan)
+	<-exitChan
+
+	if formatErr != nil {
+		return "", exitCode, errors.Wrap(err, "formatting")
+	}
+
+	return output, exitCode, nil
+}
+
+func getPackages(includePatterns []string, excludePatterns ArrayFlags) ([][]string, error) {
+	globs := normalizeSplit(includePatterns)
+	if len(globs) == 0 {
+		globs = append(globs, ".")
+	}
+
+	packages, err := dots.ResolvePackages(globs, normalizeSplit(excludePatterns))
+	if err != nil {
+		return nil, errors.Wrap(err, "getting packages - resolving packages in dots")
+	}
+
+	return packages, nil
+}
+
+func normalizeSplit(strs []string) []string {
+	res := []string{}
+
+	for _, s := range strs {
+		t := strings.Trim(s, " \t")
+		if len(t) > 0 {
+			res = append(res, t)
+		}
+	}
+
+	return res
+}
+
+// ArrayFlags type for string list.
+type ArrayFlags []string
+
+func (i *ArrayFlags) String() string {
+	return strings.Join([]string(*i), " ")
+}
+
+// Set value for array flags.
+func (i *ArrayFlags) Set(value string) error {
+	*i = append(*i, value)
+
+	return nil
+}
diff --git a/revivelib/extra_rule.go b/revivelib/extra_rule.go
new file mode 100644
index 0000000..3e34e54
--- /dev/null
+++ b/revivelib/extra_rule.go
@@ -0,0 +1,17 @@
+package revivelib
+
+import "github.com/mgechev/revive/lint"
+
+// ExtraRule configures a new rule to be used with revive.
+type ExtraRule struct {
+	Rule          lint.Rule
+	DefaultConfig lint.RuleConfig
+}
+
+// NewExtraRule returns a configured extra rule.
+func NewExtraRule(rule lint.Rule, defaultConfig lint.RuleConfig) ExtraRule {
+	return ExtraRule{
+		Rule:          rule,
+		DefaultConfig: defaultConfig,
+	}
+}
diff --git a/revivelib/pattern.go b/revivelib/pattern.go
new file mode 100644
index 0000000..fe87fc0
--- /dev/null
+++ b/revivelib/pattern.go
@@ -0,0 +1,33 @@
+package revivelib
+
+// LintPattern indicates a pattern to be included/excluded when linting
+type LintPattern struct {
+	isExclude bool
+	pattern   string
+}
+
+// IsExclude - should this pattern be included or excluded when linting
+func (p *LintPattern) IsExclude() bool {
+	return p.isExclude
+}
+
+// GetPattern - returns the actual pattern
+func (p *LintPattern) GetPattern() string {
+	return p.pattern
+}
+
+// Include this pattern when linting
+func Include(pattern string) *LintPattern {
+	return &LintPattern{
+		isExclude: false,
+		pattern:   pattern,
+	}
+}
+
+// Exclude this pattern when linting
+func Exclude(pattern string) *LintPattern {
+	return &LintPattern{
+		isExclude: true,
+		pattern:   pattern,
+	}
+}
diff --git a/rule/redefines-builtin-id.go b/rule/redefines-builtin-id.go
index da6139f..de46e29 100644
--- a/rule/redefines-builtin-id.go
+++ b/rule/redefines-builtin-id.go
@@ -8,6 +8,60 @@ import (
 	"github.com/mgechev/revive/lint"
 )
 
+var builtInConstAndVars = map[string]bool{
+	"true":  true,
+	"false": true,
+	"iota":  true,
+	"nil":   true,
+}
+
+var builtFunctions = map[string]bool{
+	"append":  true,
+	"cap":     true,
+	"close":   true,
+	"complex": true,
+	"copy":    true,
+	"delete":  true,
+	"imag":    true,
+	"len":     true,
+	"make":    true,
+	"new":     true,
+	"panic":   true,
+	"print":   true,
+	"println": true,
+	"real":    true,
+	"recover": true,
+}
+
+var builtInTypes = map[string]bool{
+	"ComplexType": true,
+	"FloatType":   true,
+	"IntegerType": true,
+	"Type":        true,
+	"Type1":       true,
+	"bool":        true,
+	"byte":        true,
+	"complex128":  true,
+	"complex64":   true,
+	"error":       true,
+	"float32":     true,
+	"float64":     true,
+	"int":         true,
+	"int16":       true,
+	"int32":       true,
+	"int64":       true,
+	"int8":        true,
+	"rune":        true,
+	"string":      true,
+	"uint":        true,
+	"uint16":      true,
+	"uint32":      true,
+	"uint64":      true,
+	"uint8":       true,
+	"uintptr":     true,
+	"any":         true,
+}
+
 // RedefinesBuiltinIDRule warns when a builtin identifier is shadowed.
 type RedefinesBuiltinIDRule struct{}
 
@@ -15,65 +69,12 @@ type RedefinesBuiltinIDRule struct{}
 func (r *RedefinesBuiltinIDRule) Apply(file *lint.File, _ lint.Arguments) []lint.Failure {
 	var failures []lint.Failure
 
-	builtInConstAndVars := map[string]bool{
-		"true":  true,
-		"false": true,
-		"iota":  true,
-		"nil":   true,
-	}
-
-	builtFunctions := map[string]bool{
-		"append":  true,
-		"cap":     true,
-		"close":   true,
-		"complex": true,
-		"copy":    true,
-		"delete":  true,
-		"imag":    true,
-		"len":     true,
-		"make":    true,
-		"new":     true,
-		"panic":   true,
-		"print":   true,
-		"println": true,
-		"real":    true,
-		"recover": true,
-	}
-
-	builtInTypes := map[string]bool{
-		"ComplexType": true,
-		"FloatType":   true,
-		"IntegerType": true,
-		"Type":        true,
-		"Type1":       true,
-		"bool":        true,
-		"byte":        true,
-		"complex128":  true,
-		"complex64":   true,
-		"error":       true,
-		"float32":     true,
-		"float64":     true,
-		"int":         true,
-		"int16":       true,
-		"int32":       true,
-		"int64":       true,
-		"int8":        true,
-		"rune":        true,
-		"string":      true,
-		"uint":        true,
-		"uint16":      true,
-		"uint32":      true,
-		"uint64":      true,
-		"uint8":       true,
-		"uintptr":     true,
-	}
-
 	onFailure := func(failure lint.Failure) {
 		failures = append(failures, failure)
 	}
 
 	astFile := file.AST
-	w := &lintRedefinesBuiltinID{builtInConstAndVars, builtFunctions, builtInTypes, onFailure}
+	w := &lintRedefinesBuiltinID{onFailure}
 	ast.Walk(w, astFile)
 
 	return failures
@@ -85,34 +86,46 @@ func (r *RedefinesBuiltinIDRule) Name() string {
 }
 
 type lintRedefinesBuiltinID struct {
-	constsAndVars map[string]bool
-	funcs         map[string]bool
-	types         map[string]bool
-	onFailure     func(lint.Failure)
+	onFailure func(lint.Failure)
 }
 
 func (w *lintRedefinesBuiltinID) Visit(node ast.Node) ast.Visitor {
 	switch n := node.(type) {
 	case *ast.GenDecl:
-		if n.Tok != token.TYPE {
-			return nil // skip if not type declaration
-		}
-		typeSpec, ok := n.Specs[0].(*ast.TypeSpec)
-		if !ok {
-			return nil
-		}
-		id := typeSpec.Name.Name
-		if w.types[id] {
-			w.addFailure(n, fmt.Sprintf("redefinition of the built-in type %s", id))
+		switch n.Tok {
+		case token.TYPE:
+			typeSpec, ok := n.Specs[0].(*ast.TypeSpec)
+			if !ok {
+				return nil
+			}
+			id := typeSpec.Name.Name
+			if ok, bt := w.isBuiltIn(id); ok {
+				w.addFailure(n, fmt.Sprintf("redefinition of the built-in %s %s", bt, id))
+			}
+		case token.VAR, token.CONST:
+			for _, vs := range n.Specs {
+				valSpec, ok := vs.(*ast.ValueSpec)
+				if !ok {
+					continue
+				}
+				for _, name := range valSpec.Names {
+					if ok, bt := w.isBuiltIn(name.Name); ok {
+						w.addFailure(n, fmt.Sprintf("redefinition of the built-in %s %s", bt, name))
+					}
+				}
+			}
+		default:
+			return nil // skip if not type/var/const declaration
 		}
+
 	case *ast.FuncDecl:
 		if n.Recv != nil {
 			return w // skip methods
 		}
 
 		id := n.Name.Name
-		if w.funcs[id] {
-			w.addFailure(n, fmt.Sprintf("redefinition of the built-in function %s", id))
+		if ok, bt := w.isBuiltIn(id); ok {
+			w.addFailure(n, fmt.Sprintf("redefinition of the built-in %s %s", bt, id))
 		}
 	case *ast.AssignStmt:
 		for _, e := range n.Lhs {
@@ -121,13 +134,20 @@ func (w *lintRedefinesBuiltinID) Visit(node ast.Node) ast.Visitor {
 				continue
 			}
 
-			if w.constsAndVars[id.Name] {
+			if ok, bt := w.isBuiltIn(id.Name); ok {
 				var msg string
-				if n.Tok == token.DEFINE {
-					msg = fmt.Sprintf("assignment creates a shadow of built-in identifier %s", id.Name)
-				} else {
-					msg = fmt.Sprintf("assignment modifies built-in identifier %s", id.Name)
+				println(bt, id.Name)
+				switch bt {
+				case "constant or variable":
+					if n.Tok == token.DEFINE {
+						msg = fmt.Sprintf("assignment creates a shadow of built-in identifier %s", id.Name)
+					} else {
+						msg = fmt.Sprintf("assignment modifies built-in identifier %s", id.Name)
+					}
+				default:
+					msg = fmt.Sprintf("redefinition of the built-in %s %s", bt, id)
 				}
+
 				w.addFailure(n, msg)
 			}
 		}
@@ -144,3 +164,19 @@ func (w lintRedefinesBuiltinID) addFailure(node ast.Node, msg string) {
 		Failure:    msg,
 	})
 }
+
+func (w lintRedefinesBuiltinID) isBuiltIn(id string) (r bool, builtInKind string) {
+	if builtFunctions[id] {
+		return true, "function"
+	}
+
+	if builtInConstAndVars[id] {
+		return true, "constant or variable"
+	}
+
+	if builtInTypes[id] {
+		return true, "type"
+	}
+
+	return false, ""
+}


Test patch:
diff --git a/revivelib/core_internal_test.go b/revivelib/core_internal_test.go
new file mode 100644
index 0000000..ecf00da
--- /dev/null
+++ b/revivelib/core_internal_test.go
@@ -0,0 +1,69 @@
+package revivelib
+
+import (
+	"testing"
+
+	"github.com/mgechev/revive/config"
+	"github.com/mgechev/revive/lint"
+)
+
+func TestReviveCreateInstance(t *testing.T) {
+	revive := getMockRevive(t)
+
+	if revive.config == nil {
+		t.Fatal("Could not load config.")
+	}
+
+	if revive.maxOpenFiles != 2048 {
+		t.Fatal("Expected MaxOpenFiles to be 2048")
+	}
+
+	if revive.lintingRules == nil || len(revive.lintingRules) == 0 {
+		t.Fatal("Linting rules not loaded.")
+	}
+
+	rules := map[string]lint.Rule{}
+	for _, rule := range revive.lintingRules {
+		rules[rule.Name()] = rule
+	}
+
+	if _, ok := rules["mock-rule"]; !ok {
+		t.Fatal("Didn't load mock rule.")
+	}
+
+	if revive.config.ErrorCode != 1 && revive.config.WarningCode != 1 {
+		t.Fatal("Didn't set the codes in the config instance.")
+	}
+}
+
+type mockRule struct {
+}
+
+func (r *mockRule) Name() string {
+	return "mock-rule"
+}
+
+func (r *mockRule) Apply(file *lint.File, arguments lint.Arguments) []lint.Failure {
+	return nil
+}
+
+func getMockRevive(t *testing.T) *Revive {
+	t.Helper()
+
+	conf, err := config.GetConfig("../defaults.toml")
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	revive, err := New(
+		conf,
+		true,
+		2048,
+		NewExtraRule(&mockRule{}, lint.RuleConfig{}),
+	)
+	if err != nil {
+		t.Fatal(err.Error())
+	}
+
+	return revive
+}
diff --git a/revivelib/core_test.go b/revivelib/core_test.go
new file mode 100644
index 0000000..aa0ddb4
--- /dev/null
+++ b/revivelib/core_test.go
@@ -0,0 +1,106 @@
+package revivelib_test
+
+import (
+	"strings"
+	"testing"
+
+	"github.com/mgechev/revive/config"
+	"github.com/mgechev/revive/lint"
+	"github.com/mgechev/revive/revivelib"
+)
+
+func TestReviveLint(t *testing.T) {
+	// ARRANGE
+	revive := getMockRevive(t)
+
+	// ACT
+	failures, err := revive.Lint(revivelib.Include("../testdata/if-return.go"))
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	// ASSERT
+	failureList := []lint.Failure{}
+
+	for failure := range failures {
+		failureList = append(failureList, failure)
+	}
+
+	const expected = 3
+
+	got := len(failureList)
+	if got != expected {
+		t.Fatalf("Expected failures to have %d failures, but it has %d.", expected, got)
+	}
+
+	errmsg := "redundant if ...; err != nil check, just return error instead."
+	if failureList[0].Failure != errmsg {
+		t.Fatalf("Expected failure[0] to be '%s', but it was '%s'", errmsg, failureList[0].Failure)
+	}
+}
+
+func TestReviveFormat(t *testing.T) {
+	// ARRANGE
+	revive := getMockRevive(t)
+
+	failuresChan, err := revive.Lint(revivelib.Include("../testdata/if-return.go"))
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	// ACT
+	failures, exitCode, err := revive.Format("stylish", failuresChan)
+
+	// ASSERT
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	errorMsgs := []string{
+		"(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.",
+	}
+	for _, errorMsg := range errorMsgs {
+		if !strings.Contains(failures, errorMsg) {
+			t.Fatalf("Expected formatted failures '%s' to contain '%s', but it didn't.", failures, errorMsg)
+		}
+	}
+
+	const expected = 1
+	if exitCode != expected {
+		t.Fatalf("Expected exit code to be %d, but it was %d.", expected, exitCode)
+	}
+}
+
+type mockRule struct {
+}
+
+func (r *mockRule) Name() string {
+	return "mock-rule"
+}
+
+func (r *mockRule) Apply(file *lint.File, arguments lint.Arguments) []lint.Failure {
+	return nil
+}
+
+func getMockRevive(t *testing.T) *revivelib.Revive {
+	t.Helper()
+
+	conf, err := config.GetConfig("../defaults.toml")
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	revive, err := revivelib.New(
+		conf,
+		true,
+		2048,
+		revivelib.NewExtraRule(&mockRule{}, lint.RuleConfig{}),
+	)
+	if err != nil {
+		t.Fatal(err.Error())
+	}
+
+	return revive
+}
diff --git a/testdata/redefines-builtin-id.go b/testdata/redefines-builtin-id.go
index 5b25f02..8654523 100644
--- a/testdata/redefines-builtin-id.go
+++ b/testdata/redefines-builtin-id.go
@@ -19,3 +19,13 @@ func delete(set []int64, i int) (y []int64) { // MATCH /redefinition of the buil
 	}
 	return
 }
+
+type any int // MATCH /redefinition of the built-in type any/
+
+func any() {} // MATCH /redefinition of the built-in type any/
+
+var any int // MATCH /redefinition of the built-in type any/
+
+const any = 1 // MATCH /redefinition of the built-in type any/
+
+var i, copy int // MATCH /redefinition of the built-in function copy/