 bf27481efd
			
		
	
	
		bf27481efd
		
			
		
	
	
	
	
		
			
			full diff: https://github.com/dominikh/go-tools/compare/2019.2.3...2020.1.3 Also updates tests to accomodate updated rules: --- FAIL: TestSourcesFromTestdataWithIssuesDir/staticcheck.go (0.43s) linters_test.go:137: [run --disable-all --print-issued-lines=false --print-linter-name=false --out-format=line-number --max-same-issues=10 -Estaticcheck --no-config testdata/staticcheck.go] linters_test.go:33: Error Trace: linters_test.go:33 linters_test.go:138 linters_test.go:53 Error: Received unexpected error: staticcheck.go:11: no match for `self-assignment of x to x` vs ["SA4006: this value of `x` is never used"] in: staticcheck.go:11:2: SA4006: this value of `x` is never used unmatched errors staticcheck.go:11:2: SA4006: this value of `x` is never used Test: TestSourcesFromTestdataWithIssuesDir/staticcheck.go Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
		
			
				
	
	
		
			222 lines
		
	
	
		
			3.4 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			222 lines
		
	
	
		
			3.4 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| package pattern
 | |
| 
 | |
| import (
 | |
| 	"fmt"
 | |
| 	"go/token"
 | |
| 	"unicode"
 | |
| 	"unicode/utf8"
 | |
| )
 | |
| 
 | |
| type lexer struct {
 | |
| 	f *token.File
 | |
| 
 | |
| 	input string
 | |
| 	start int
 | |
| 	pos   int
 | |
| 	width int
 | |
| 	items chan item
 | |
| }
 | |
| 
 | |
| type itemType int
 | |
| 
 | |
| const eof = -1
 | |
| 
 | |
| const (
 | |
| 	itemError itemType = iota
 | |
| 	itemLeftParen
 | |
| 	itemRightParen
 | |
| 	itemLeftBracket
 | |
| 	itemRightBracket
 | |
| 	itemTypeName
 | |
| 	itemVariable
 | |
| 	itemAt
 | |
| 	itemColon
 | |
| 	itemBlank
 | |
| 	itemString
 | |
| 	itemEOF
 | |
| )
 | |
| 
 | |
| func (typ itemType) String() string {
 | |
| 	switch typ {
 | |
| 	case itemError:
 | |
| 		return "ERROR"
 | |
| 	case itemLeftParen:
 | |
| 		return "("
 | |
| 	case itemRightParen:
 | |
| 		return ")"
 | |
| 	case itemLeftBracket:
 | |
| 		return "["
 | |
| 	case itemRightBracket:
 | |
| 		return "]"
 | |
| 	case itemTypeName:
 | |
| 		return "TYPE"
 | |
| 	case itemVariable:
 | |
| 		return "VAR"
 | |
| 	case itemAt:
 | |
| 		return "@"
 | |
| 	case itemColon:
 | |
| 		return ":"
 | |
| 	case itemBlank:
 | |
| 		return "_"
 | |
| 	case itemString:
 | |
| 		return "STRING"
 | |
| 	case itemEOF:
 | |
| 		return "EOF"
 | |
| 	default:
 | |
| 		return fmt.Sprintf("itemType(%d)", typ)
 | |
| 	}
 | |
| }
 | |
| 
 | |
| type item struct {
 | |
| 	typ itemType
 | |
| 	val string
 | |
| 	pos int
 | |
| }
 | |
| 
 | |
| type stateFn func(*lexer) stateFn
 | |
| 
 | |
| func (l *lexer) run() {
 | |
| 	for state := lexStart; state != nil; {
 | |
| 		state = state(l)
 | |
| 	}
 | |
| 	close(l.items)
 | |
| }
 | |
| 
 | |
| func (l *lexer) emitValue(t itemType, value string) {
 | |
| 	l.items <- item{t, value, l.start}
 | |
| 	l.start = l.pos
 | |
| }
 | |
| 
 | |
| func (l *lexer) emit(t itemType) {
 | |
| 	l.items <- item{t, l.input[l.start:l.pos], l.start}
 | |
| 	l.start = l.pos
 | |
| }
 | |
| 
 | |
| func lexStart(l *lexer) stateFn {
 | |
| 	switch r := l.next(); {
 | |
| 	case r == eof:
 | |
| 		l.emit(itemEOF)
 | |
| 		return nil
 | |
| 	case unicode.IsSpace(r):
 | |
| 		l.ignore()
 | |
| 	case r == '(':
 | |
| 		l.emit(itemLeftParen)
 | |
| 	case r == ')':
 | |
| 		l.emit(itemRightParen)
 | |
| 	case r == '[':
 | |
| 		l.emit(itemLeftBracket)
 | |
| 	case r == ']':
 | |
| 		l.emit(itemRightBracket)
 | |
| 	case r == '@':
 | |
| 		l.emit(itemAt)
 | |
| 	case r == ':':
 | |
| 		l.emit(itemColon)
 | |
| 	case r == '_':
 | |
| 		l.emit(itemBlank)
 | |
| 	case r == '"':
 | |
| 		l.backup()
 | |
| 		return lexString
 | |
| 	case unicode.IsUpper(r):
 | |
| 		l.backup()
 | |
| 		return lexType
 | |
| 	case unicode.IsLower(r):
 | |
| 		l.backup()
 | |
| 		return lexVariable
 | |
| 	default:
 | |
| 		return l.errorf("unexpected character %c", r)
 | |
| 	}
 | |
| 	return lexStart
 | |
| }
 | |
| 
 | |
| func (l *lexer) next() (r rune) {
 | |
| 	if l.pos >= len(l.input) {
 | |
| 		l.width = 0
 | |
| 		return eof
 | |
| 	}
 | |
| 	r, l.width = utf8.DecodeRuneInString(l.input[l.pos:])
 | |
| 
 | |
| 	if r == '\n' {
 | |
| 		l.f.AddLine(l.pos)
 | |
| 	}
 | |
| 
 | |
| 	l.pos += l.width
 | |
| 
 | |
| 	return r
 | |
| }
 | |
| 
 | |
| func (l *lexer) ignore() {
 | |
| 	l.start = l.pos
 | |
| }
 | |
| 
 | |
| func (l *lexer) backup() {
 | |
| 	l.pos -= l.width
 | |
| }
 | |
| 
 | |
| func (l *lexer) errorf(format string, args ...interface{}) stateFn {
 | |
| 	// TODO(dh): emit position information in errors
 | |
| 	l.items <- item{
 | |
| 		itemError,
 | |
| 		fmt.Sprintf(format, args...),
 | |
| 		l.start,
 | |
| 	}
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| func isAlphaNumeric(r rune) bool {
 | |
| 	return r >= '0' && r <= '9' ||
 | |
| 		r >= 'a' && r <= 'z' ||
 | |
| 		r >= 'A' && r <= 'Z'
 | |
| }
 | |
| 
 | |
| func lexString(l *lexer) stateFn {
 | |
| 	l.next() // skip quote
 | |
| 	escape := false
 | |
| 
 | |
| 	var runes []rune
 | |
| 	for {
 | |
| 		switch r := l.next(); r {
 | |
| 		case eof:
 | |
| 			return l.errorf("unterminated string")
 | |
| 		case '"':
 | |
| 			if !escape {
 | |
| 				l.emitValue(itemString, string(runes))
 | |
| 				return lexStart
 | |
| 			} else {
 | |
| 				runes = append(runes, '"')
 | |
| 				escape = false
 | |
| 			}
 | |
| 		case '\\':
 | |
| 			if escape {
 | |
| 				runes = append(runes, '\\')
 | |
| 				escape = false
 | |
| 			} else {
 | |
| 				escape = true
 | |
| 			}
 | |
| 		default:
 | |
| 			runes = append(runes, r)
 | |
| 		}
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func lexType(l *lexer) stateFn {
 | |
| 	l.next()
 | |
| 	for {
 | |
| 		if !isAlphaNumeric(l.next()) {
 | |
| 			l.backup()
 | |
| 			l.emit(itemTypeName)
 | |
| 			return lexStart
 | |
| 		}
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func lexVariable(l *lexer) stateFn {
 | |
| 	l.next()
 | |
| 	for {
 | |
| 		if !isAlphaNumeric(l.next()) {
 | |
| 			l.backup()
 | |
| 			l.emit(itemVariable)
 | |
| 			return lexStart
 | |
| 		}
 | |
| 	}
 | |
| }
 |