From e331de98f9f1c58ddf5365565b9317884e9506db Mon Sep 17 00:00:00 2001 From: Etienne Audet-Cobello Date: Sun, 16 Nov 2025 20:16:17 -0500 Subject: [PATCH] internal/importpath: extract shared import path validation logic --- src/cmd/go/internal/modindex/build_read.go | 18 ++--------- src/internal/importpath/importpath.go | 28 +++++++++++++++++ src/internal/importpath/importpath_test.go | 36 ++++++++++++++++++++++ 3 files changed, 66 insertions(+), 16 deletions(-) create mode 100644 src/internal/importpath/importpath.go create mode 100644 src/internal/importpath/importpath_test.go diff --git a/src/cmd/go/internal/modindex/build_read.go b/src/cmd/go/internal/modindex/build_read.go index f2e48f36cdff54..a548b7defb4443 100644 --- a/src/cmd/go/internal/modindex/build_read.go +++ b/src/cmd/go/internal/modindex/build_read.go @@ -17,10 +17,10 @@ import ( "go/parser" "go/scanner" "go/token" + "internal/importpath" "io" "strconv" "strings" - "unicode" "unicode/utf8" ) @@ -325,7 +325,7 @@ func readGoInfo(f io.Reader, info *fileInfo) error { if err != nil { return fmt.Errorf("parser returned invalid quoted string: <%s>", quoted) } - if !isValidImport(path) { + if !(importpath.IsValidImport(path)) { // The parser used to return a parse error for invalid import paths, but // no longer does, so check for and create the error here instead. info.parseErr = scanner.Error{Pos: info.fset.Position(spec.Pos()), Msg: "invalid import path: " + path} @@ -389,20 +389,6 @@ func readGoInfo(f io.Reader, info *fileInfo) error { return nil } -// isValidImport checks if the import is a valid import using the more strict -// checks allowed by the implementation restriction in https://go.dev/ref/spec#Import_declarations. -// It was ported from the function of the same name that was removed from the -// parser in CL 424855, when the parser stopped doing these checks. -func isValidImport(s string) bool { - const illegalChars = `!"#$%&'()*,:;<=>?[\]^{|}` + "`\uFFFD" - for _, r := range s { - if !unicode.IsGraphic(r) || unicode.IsSpace(r) || strings.ContainsRune(illegalChars, r) { - return false - } - } - return s != "" -} - // parseGoEmbed parses a "//go:embed" to extract the glob patterns. // It accepts unquoted space-separated patterns as well as double-quoted and back-quoted Go strings. // This must match the behavior of cmd/compile/internal/noder.go. diff --git a/src/internal/importpath/importpath.go b/src/internal/importpath/importpath.go new file mode 100644 index 00000000000000..9c5096246d638c --- /dev/null +++ b/src/internal/importpath/importpath.go @@ -0,0 +1,28 @@ +// Copyright 2025 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 importpath + +import ( + "strings" + "unicode" +) + +const illegalChars = `!"#$%&'()*,:;<=>?[\]^{|}` + "`\uFFFD" + +func isValidImportPathChar(r rune) bool { + return unicode.IsGraphic(r) && !unicode.IsSpace(r) && !strings.ContainsRune(illegalChars, r) +} + +// IsValidImport checks if the import is a valid import using the more strict +// checks allowed by the implementation restriction in https://go.dev/ref/spec#Import_declarations. +// This logic was previously duplicated across several packages. +func IsValidImport(s string) bool { + for _, r := range s { + if !isValidImportPathChar(r) { + return false + } + } + return s != "" +} diff --git a/src/internal/importpath/importpath_test.go b/src/internal/importpath/importpath_test.go new file mode 100644 index 00000000000000..bbc838b0ff74ab --- /dev/null +++ b/src/internal/importpath/importpath_test.go @@ -0,0 +1,36 @@ +// Copyright 2025 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 importpath + +import ( + "testing" +) + +func TestImportPath(t *testing.T) { + tests := []string{ + "net", + "net/http", + "a/b/c", + } + for _, tc := range tests { + if !IsValidImport(tc) { + t.Errorf("expected %q to be valid import path", tc) + } + } +} + +func TestImportPathInvalid(t *testing.T) { + tests := []string{ + "", + "foo bar", + "\uFFFD", + "hello!", + } + for _, tc := range tests { + if IsValidImport(tc) { + t.Errorf("expected %q to be invalid import path", tc) + } + } +}