"go/ast"
"go/scanner"
"go/token"
+ "strconv"
+ "strings"
+ "unicode"
)
// The parser structure holds the parser's internal state.
type parseSpecFunction func(p *parser, doc *ast.CommentGroup, iota int) ast.Spec
+func isValidImport(lit string) bool {
+ const illegalChars = `!"#$%&'()*,:;<=>?[\]^{|}` + "`\uFFFD"
+ s, _ := strconv.Unquote(lit) // go/scanner returns a legal string literal
+ for _, r := range s {
+ if !unicode.IsGraphic(r) || unicode.IsSpace(r) || strings.ContainsRune(illegalChars, r) {
+ return false
+ }
+ }
+ return s != ""
+}
+
func parseImportSpec(p *parser, doc *ast.CommentGroup, _ int) ast.Spec {
if p.trace {
defer un(trace(p, "ImportSpec"))
var path *ast.BasicLit
if p.tok == token.STRING {
+ if !isValidImport(p.lit) {
+ p.error(p.pos, "invalid import path: "+p.lit)
+ }
path = &ast.BasicLit{ValuePos: p.pos, Kind: p.tok, Value: p.lit}
p.next()
} else {
package parser
import (
+ "fmt"
"go/ast"
"go/token"
"os"
}
}
}
+
+var imports = map[string]bool{
+ "a": true,
+ "a/b": true,
+ "a.b": true,
+ "m\x61th": true,
+ "greek/αβ": true,
+ "": false,
+ "\x00": false,
+ "\x7f": false,
+ "a!": false,
+ "a b": false,
+ `a\b`: false,
+ "`a`": false,
+ "\x80\x80": false,
+}
+
+func TestImports(t *testing.T) {
+ for path, isValid := range imports {
+ src := fmt.Sprintf("package p; import %q", path)
+ _, err := ParseFile(fset, "", src, 0)
+ switch {
+ case err != nil && isValid:
+ t.Errorf("ParseFile(%s): got %v; expected no error", src, err)
+ case err == nil && !isValid:
+ t.Errorf("ParseFile(%s): got no error; expected one", src)
+ }
+ }
+}