summaryrefslogtreecommitdiffstats
path: root/vendor/github.com/mgechev/revive/rule/redefines-builtin-id.go
blob: 947b8aac7c40f54d4045dddfc8f4a2cc5b763e44 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
package rule

import (
	"fmt"
	"github.com/mgechev/revive/lint"
	"go/ast"
	"go/token"
)

// RedefinesBuiltinIDRule warns when a builtin identifier is shadowed.
type RedefinesBuiltinIDRule struct{}

// Apply applies the rule to given file.
func (r *RedefinesBuiltinIDRule) Apply(file *lint.File, _ lint.Arguments) []lint.Failure {
	var failures []lint.Failure

	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,
	}

	onFailure := func(failure lint.Failure) {
		failures = append(failures, failure)
	}

	astFile := file.AST
	w := &lintRedefinesBuiltinID{builtInConstAndVars, builtFunctions, builtInTypes, onFailure}
	ast.Walk(w, astFile)

	return failures
}

// Name returns the rule name.
func (r *RedefinesBuiltinIDRule) Name() string {
	return "redefines-builtin-id"
}

type lintRedefinesBuiltinID struct {
	constsAndVars map[string]bool
	funcs         map[string]bool
	types         map[string]bool
	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))
		}
	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))
		}
	case *ast.AssignStmt:
		for _, e := range n.Lhs {
			id, ok := e.(*ast.Ident)
			if !ok {
				continue
			}

			if w.constsAndVars[id.Name] {
				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)
				}
				w.addFailure(n, msg)
			}
		}
	}

	return w
}

func (w lintRedefinesBuiltinID) addFailure(node ast.Node, msg string) {
	w.onFailure(lint.Failure{
		Confidence: 1,
		Node:       node,
		Category:   "logic",
		Failure:    msg,
	})
}