aboutsummaryrefslogtreecommitdiffstats
path: root/vendor/github.com/ultraware/whitespace/main.go
diff options
context:
space:
mode:
authorDmitry Vyukov <dvyukov@google.com>2020-07-04 11:12:55 +0200
committerDmitry Vyukov <dvyukov@google.com>2020-07-04 15:05:30 +0200
commitc7d7f10bdff703e4a3c0414e8a33d4e45c91eb35 (patch)
tree0dff0ee1f98dbfa3ad8776112053a450d176592b /vendor/github.com/ultraware/whitespace/main.go
parent9573094ce235bd9afe88f5da27a47dd6bcc1e13b (diff)
go.mod: vendor golangci-lint
Diffstat (limited to 'vendor/github.com/ultraware/whitespace/main.go')
-rw-r--r--vendor/github.com/ultraware/whitespace/main.go158
1 files changed, 158 insertions, 0 deletions
diff --git a/vendor/github.com/ultraware/whitespace/main.go b/vendor/github.com/ultraware/whitespace/main.go
new file mode 100644
index 000000000..c36086c0e
--- /dev/null
+++ b/vendor/github.com/ultraware/whitespace/main.go
@@ -0,0 +1,158 @@
+package whitespace
+
+import (
+ "go/ast"
+ "go/token"
+)
+
+// Message contains a message
+type Message struct {
+ Pos token.Position
+ Type MessageType
+ Message string
+}
+
+// MessageType describes what should happen to fix the warning
+type MessageType uint8
+
+// List of MessageTypes
+const (
+ MessageTypeLeading MessageType = iota + 1
+ MessageTypeTrailing
+ MessageTypeAddAfter
+)
+
+// Settings contains settings for edge-cases
+type Settings struct {
+ MultiIf bool
+ MultiFunc bool
+}
+
+// Run runs this linter on the provided code
+func Run(file *ast.File, fset *token.FileSet, settings Settings) []Message {
+ var messages []Message
+
+ for _, f := range file.Decls {
+ decl, ok := f.(*ast.FuncDecl)
+ if !ok || decl.Body == nil { // decl.Body can be nil for e.g. cgo
+ continue
+ }
+
+ vis := visitor{file.Comments, fset, nil, make(map[*ast.BlockStmt]bool), settings}
+ ast.Walk(&vis, decl)
+
+ messages = append(messages, vis.messages...)
+ }
+
+ return messages
+}
+
+type visitor struct {
+ comments []*ast.CommentGroup
+ fset *token.FileSet
+ messages []Message
+ wantNewline map[*ast.BlockStmt]bool
+ settings Settings
+}
+
+func (v *visitor) Visit(node ast.Node) ast.Visitor {
+ if node == nil {
+ return v
+ }
+
+ if stmt, ok := node.(*ast.IfStmt); ok && v.settings.MultiIf {
+ checkMultiLine(v, stmt.Body, stmt.Cond)
+ }
+
+ if stmt, ok := node.(*ast.FuncDecl); ok && v.settings.MultiFunc {
+ checkMultiLine(v, stmt.Body, stmt.Type)
+ }
+
+ if stmt, ok := node.(*ast.BlockStmt); ok {
+ wantNewline := v.wantNewline[stmt]
+
+ comments := v.comments
+ if wantNewline {
+ comments = nil // Comments also count as a newline if we want a newline
+ }
+ first, last := firstAndLast(comments, v.fset, stmt.Pos(), stmt.End(), stmt.List)
+
+ startMsg := checkStart(v.fset, stmt.Lbrace, first)
+
+ if wantNewline && startMsg == nil {
+ v.messages = append(v.messages, Message{v.fset.Position(stmt.Pos()), MessageTypeAddAfter, `multi-line statement should be followed by a newline`})
+ } else if !wantNewline && startMsg != nil {
+ v.messages = append(v.messages, *startMsg)
+ }
+
+ if msg := checkEnd(v.fset, stmt.Rbrace, last); msg != nil {
+ v.messages = append(v.messages, *msg)
+ }
+ }
+
+ return v
+}
+
+func checkMultiLine(v *visitor, body *ast.BlockStmt, stmtStart ast.Node) {
+ start, end := posLine(v.fset, stmtStart.Pos()), posLine(v.fset, stmtStart.End())
+
+ if end > start { // Check only multi line conditions
+ v.wantNewline[body] = true
+ }
+}
+
+func posLine(fset *token.FileSet, pos token.Pos) int {
+ return fset.Position(pos).Line
+}
+
+func firstAndLast(comments []*ast.CommentGroup, fset *token.FileSet, start, end token.Pos, stmts []ast.Stmt) (ast.Node, ast.Node) {
+ if len(stmts) == 0 {
+ return nil, nil
+ }
+
+ first, last := ast.Node(stmts[0]), ast.Node(stmts[len(stmts)-1])
+
+ for _, c := range comments {
+ if posLine(fset, c.Pos()) == posLine(fset, start) || posLine(fset, c.End()) == posLine(fset, end) {
+ continue
+ }
+
+ if c.Pos() < start || c.End() > end {
+ continue
+ }
+ if c.Pos() < first.Pos() {
+ first = c
+ }
+ if c.End() > last.End() {
+ last = c
+ }
+ }
+
+ return first, last
+}
+
+func checkStart(fset *token.FileSet, start token.Pos, first ast.Node) *Message {
+ if first == nil {
+ return nil
+ }
+
+ if posLine(fset, start)+1 < posLine(fset, first.Pos()) {
+ pos := fset.Position(start)
+ return &Message{pos, MessageTypeLeading, `unnecessary leading newline`}
+ }
+
+ return nil
+}
+
+func checkEnd(fset *token.FileSet, end token.Pos, last ast.Node) *Message {
+ if last == nil {
+ return nil
+ }
+
+ if posLine(fset, end)-1 > posLine(fset, last.End()) {
+ pos := fset.Position(end)
+ return &Message{pos, MessageTypeTrailing, `unnecessary trailing newline`}
+ }
+
+ return nil
+}