"bytes"
"go/ast"
"go/token"
+ "strconv"
+ "strings"
+ "unicode"
"unicode/utf8"
)
}
}
+func sanitizeImportPath(lit *ast.BasicLit) *ast.BasicLit {
+ // Note: An unmodified AST generated by go/parser will already
+ // contain a backward- or double-quoted path string that does
+ // not contain any invalid characters, and most of the work
+ // here is not needed. However, a modified or generated AST
+ // may possibly contain non-canonical paths. Do the work in
+ // all cases since it's not too hard and not speed-critical.
+
+ // if we don't have a proper string, be conservative and return whatever we have
+ if lit.Kind != token.STRING {
+ return lit
+ }
+ s, err := strconv.Unquote(lit.Value)
+ if err != nil {
+ return lit
+ }
+
+ // if the string is an invalid path, return whatever we have
+ //
+ // spec: "Implementation restriction: A compiler may restrict
+ // ImportPaths to non-empty strings using only characters belonging
+ // to Unicode's L, M, N, P, and S general categories (the Graphic
+ // characters without spaces) and may also exclude the characters
+ // !"#$%&'()*,:;<=>?[\]^`{|} and the Unicode replacement character
+ // U+FFFD."
+ if s == "" {
+ return lit
+ }
+ const illegalChars = `!"#$%&'()*,:;<=>?[\]^{|}` + "`\uFFFD"
+ for _, r := range s {
+ if !unicode.IsGraphic(r) || unicode.IsSpace(r) || strings.ContainsRune(illegalChars, r) {
+ return lit
+ }
+ }
+
+ // otherwise, return the double-quoted path
+ s = strconv.Quote(s)
+ if s == lit.Value {
+ return lit // nothing wrong with lit
+ }
+ return &ast.BasicLit{ValuePos: lit.ValuePos, Kind: token.STRING, Value: s}
+}
+
// The parameter n is the number of specs in the group. If doIndent is set,
// multi-line identifier lists in the spec are indented when the first
// linebreak is encountered.
p.expr(s.Name)
p.print(blank)
}
- p.expr(s.Path)
+ p.expr(sanitizeImportPath(s.Path))
p.setComment(s.Comment)
p.print(s.EndPos)
"package_dddd" // comment
)
+// print import paths as double-quoted strings
+// (we would like more test cases but the go/parser
+// already excludes most incorrect paths, and we don't
+// bother setting up test-ASTs manually)
+import (
+ `fmt`
+ "math"
+)
+
// at least one empty line between declarations of different kind
import _ "io"
var _ int