"log"
"os"
"sort"
- "unicode"
"cmd/internal/edit"
"cmd/internal/objabi"
return fmt.Errorf("too many options")
}
- if *varVar != "" && !isValidIdentifier(*varVar) {
+ if *varVar != "" && !token.IsIdentifier(*varVar) {
return fmt.Errorf("-var: %q is not a valid identifier", *varVar)
}
}
}
-func isValidIdentifier(ident string) bool {
- if len(ident) == 0 {
- return false
- }
- for i, c := range ident {
- if i > 0 && unicode.IsDigit(c) {
- continue
- }
- if c == '_' || unicode.IsLetter(c) {
- continue
- }
- return false
- }
- return true
-}
-
// It is possible for positions to repeat when there is a line
// directive that does not specify column information and the input
// has not been passed through gofmt.
"flag"
"fmt"
"go/build"
+ "go/token"
"io"
"log"
"os"
case 1:
case 2:
method = elem[1]
- isIdentifier(method)
+ if !token.IsIdentifier(method) {
+ log.Fatalf("invalid identifier %q", method)
+ }
default:
log.Printf("too many periods in symbol specification")
usage()
}
symbol = elem[0]
- isIdentifier(symbol)
- return
-}
-
-// isIdentifier checks that the name is valid Go identifier, and
-// logs and exits if it is not.
-func isIdentifier(name string) {
- if len(name) == 0 {
- log.Fatal("empty symbol")
- }
- for i, ch := range name {
- if unicode.IsLetter(ch) || ch == '_' || i > 0 && unicode.IsDigit(ch) {
- continue
- }
- log.Fatalf("invalid identifier %q", name)
+ if !token.IsIdentifier(symbol) {
+ log.Fatalf("invalid identifier %q", symbol)
}
+ return
}
// isExported reports whether the name is an exported identifier.
import (
"go/token"
"strings"
- "unicode"
- "unicode/utf8"
)
// ----------------------------------------------------------------------------
//
func NewIdent(name string) *Ident { return &Ident{token.NoPos, name, nil} }
-// IsExported reports whether name is an exported Go symbol
-// (that is, whether it begins with an upper-case letter).
+// IsExported reports whether name starts with an upper-case letter.
//
-func IsExported(name string) bool {
- ch, _ := utf8.DecodeRuneInString(name)
- return unicode.IsUpper(ch)
-}
+func IsExported(name string) bool { return token.IsExported(name) }
-// IsExported reports whether id is an exported Go symbol
-// (that is, whether it begins with an uppercase letter).
+// IsExported reports whether id starts with an upper-case letter.
//
-func (id *Ident) IsExported() bool { return IsExported(id.Name) }
+func (id *Ident) IsExported() bool { return token.IsExported(id.Name) }
func (id *Ident) String() string {
if id != nil {
//
package token
-import "strconv"
+import (
+ "strconv"
+ "unicode"
+ "unicode/utf8"
+)
// Token is the set of lexical tokens of the Go programming language.
type Token int
// it returns false otherwise.
//
func (tok Token) IsKeyword() bool { return keyword_beg < tok && tok < keyword_end }
+
+// IsExported reports whether name starts with an upper-case letter.
+//
+func IsExported(name string) bool {
+ ch, _ := utf8.DecodeRuneInString(name)
+ return unicode.IsUpper(ch)
+}
+
+// IsKeyword reports whether name is a Go keyword, such as "func" or "return".
+//
+func IsKeyword(name string) bool {
+ // TODO: opt: use a perfect hash function instead of a global map.
+ _, ok := keywords[name]
+ return ok
+}
+
+// IsIdentifier reports whether name is a Go identifier, that is, a non-empty
+// string made up of letters, digits, and underscores, where the first character
+// is not a digit. Keywords are not identifiers.
+//
+func IsIdentifier(name string) bool {
+ for i, c := range name {
+ if !unicode.IsLetter(c) && c != '_' && (i == 0 || !unicode.IsDigit(c)) {
+ return false
+ }
+ }
+ return name != "" && !IsKeyword(name)
+}
--- /dev/null
+// Copyright 2019 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package token
+
+import "testing"
+
+func TestIsIdentifier(t *testing.T) {
+ tests := []struct {
+ name string
+ in string
+ want bool
+ }{
+ {"Empty", "", false},
+ {"Space", " ", false},
+ {"SpaceSuffix", "foo ", false},
+ {"Number", "123", false},
+ {"Keyword", "func", false},
+
+ {"LettersASCII", "foo", true},
+ {"MixedASCII", "_bar123", true},
+ {"UppercaseKeyword", "Func", true},
+ {"LettersUnicode", "fóö", true},
+ }
+ for _, test := range tests {
+ t.Run(test.name, func(t *testing.T) {
+ if got := IsIdentifier(test.in); got != test.want {
+ t.Fatalf("IsIdentifier(%q) = %t, want %v", test.in, got, test.want)
+ }
+ })
+ }
+}