Skip to content

Commit 0090c13

Browse files
committed
cmd/go: ignore unknown directives in dependency go.mod files
This will help with forward compatibility when we add additional directives that only matter for the main module (or that can be safely ignored otherwise). Change-Id: Ida1e186fb2669b128aeb5a9a1187e2535b72b763 Reviewed-on: https://go-review.googlesource.com/125936 Reviewed-by: Bryan C. Mills <bcmills@google.com>
1 parent 9cde804 commit 0090c13

File tree

5 files changed

+88
-10
lines changed

5 files changed

+88
-10
lines changed

src/cmd/go/internal/modfile/read_test.go

+15
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,21 @@ func testPrint(t *testing.T, in, out string) {
6666
}
6767
}
6868

69+
func TestParseLax(t *testing.T) {
70+
badFile := []byte(`module m
71+
surprise attack
72+
x y (
73+
z
74+
)
75+
exclude v1.2.3
76+
replace <-!!!
77+
`)
78+
_, err := ParseLax("file", badFile, nil)
79+
if err != nil {
80+
t.Fatalf("ParseLax did not ignore irrelevant errors: %v", err)
81+
}
82+
}
83+
6984
// Test that when files in the testdata directory are parsed
7085
// and printed and parsed again, we get the same parse tree
7186
// both times.

src/cmd/go/internal/modfile/rule.go

+34-8
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,24 @@ func (f *File) AddComment(text string) {
8787

8888
type VersionFixer func(path, version string) (string, error)
8989

90+
// Parse parses the data, reported in errors as being from file,
91+
// into a File struct. It applies fix, if non-nil, to canonicalize all module versions found.
9092
func Parse(file string, data []byte, fix VersionFixer) (*File, error) {
93+
return parseToFile(file, data, fix, true)
94+
}
95+
96+
// ParseLax is like Parse but ignores unknown statements.
97+
// It is used when parsing go.mod files other than the main module,
98+
// under the theory that most statement types we add in the future will
99+
// only apply in the main module, like exclude and replace,
100+
// and so we get better gradual deployments if old go commands
101+
// simply ignore those statements when found in go.mod files
102+
// in dependencies.
103+
func ParseLax(file string, data []byte, fix VersionFixer) (*File, error) {
104+
return parseToFile(file, data, fix, false)
105+
}
106+
107+
func parseToFile(file string, data []byte, fix VersionFixer, strict bool) (*File, error) {
91108
fs, err := parse(file, data)
92109
if err != nil {
93110
return nil, err
@@ -100,20 +117,24 @@ func Parse(file string, data []byte, fix VersionFixer) (*File, error) {
100117
for _, x := range fs.Stmt {
101118
switch x := x.(type) {
102119
case *Line:
103-
f.add(&errs, x, x.Token[0], x.Token[1:], fix)
120+
f.add(&errs, x, x.Token[0], x.Token[1:], fix, strict)
104121

105122
case *LineBlock:
106123
if len(x.Token) > 1 {
107-
fmt.Fprintf(&errs, "%s:%d: unknown block type: %s\n", file, x.Start.Line, strings.Join(x.Token, " "))
124+
if strict {
125+
fmt.Fprintf(&errs, "%s:%d: unknown block type: %s\n", file, x.Start.Line, strings.Join(x.Token, " "))
126+
}
108127
continue
109128
}
110129
switch x.Token[0] {
111130
default:
112-
fmt.Fprintf(&errs, "%s:%d: unknown block type: %s\n", file, x.Start.Line, strings.Join(x.Token, " "))
131+
if strict {
132+
fmt.Fprintf(&errs, "%s:%d: unknown block type: %s\n", file, x.Start.Line, strings.Join(x.Token, " "))
133+
}
113134
continue
114135
case "module", "require", "exclude", "replace":
115136
for _, l := range x.Line {
116-
f.add(&errs, l, x.Token[0], l.Token, fix)
137+
f.add(&errs, l, x.Token[0], l.Token, fix, strict)
117138
}
118139
}
119140
}
@@ -125,15 +146,20 @@ func Parse(file string, data []byte, fix VersionFixer) (*File, error) {
125146
return f, nil
126147
}
127148

128-
func (f *File) add(errs *bytes.Buffer, line *Line, verb string, args []string, fix VersionFixer) {
129-
// TODO: We should pass in a flag saying whether this module is a dependency.
130-
// If so, we should ignore all unknown directives and not attempt to parse
149+
func (f *File) add(errs *bytes.Buffer, line *Line, verb string, args []string, fix VersionFixer, strict bool) {
150+
// If strict is false, this module is a dependency.
151+
// We ignore all unknown directives and do not attempt to parse
131152
// replace and exclude either. They don't matter, and it will work better for
132-
// forward compatibility if we can depend on modules that have local changes.
153+
// forward compatibility if we can depend on modules that have unknown
154+
// statements (presumed relevant only when acting as the main module).
155+
if !strict && verb != "module" && verb != "require" {
156+
return
157+
}
133158

134159
switch verb {
135160
default:
136161
fmt.Fprintf(errs, "%s:%d: unknown directive: %s\n", f.Syntax.Name, line.Start.Line, verb)
162+
137163
case "module":
138164
if f.Module != nil {
139165
fmt.Fprintf(errs, "%s:%d: repeated module statement\n", f.Syntax.Name, line.Start.Line)

src/cmd/go/internal/modload/load.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -764,7 +764,7 @@ func (r *mvsReqs) required(mod module.Version) ([]module.Version, error) {
764764
base.Errorf("go: parsing %s: %v", base.ShortPath(gomod), err)
765765
return nil, ErrRequire
766766
}
767-
f, err := modfile.Parse(gomod, data, nil)
767+
f, err := modfile.ParseLax(gomod, data, nil)
768768
if err != nil {
769769
base.Errorf("go: parsing %s: %v", base.ShortPath(gomod), err)
770770
return nil, ErrRequire
@@ -792,7 +792,7 @@ func (r *mvsReqs) required(mod module.Version) ([]module.Version, error) {
792792
base.Errorf("go: %s@%s: %v\n", mod.Path, mod.Version, err)
793793
return nil, ErrRequire
794794
}
795-
f, err := modfile.Parse("go.mod", data, nil)
795+
f, err := modfile.ParseLax("go.mod", data, nil)
796796
if err != nil {
797797
base.Errorf("go: %s@%s: parsing go.mod: %v", mod.Path, mod.Version, err)
798798
return nil, ErrRequire
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
rsc.io/badmod v1.0.0
2+
written by hand
3+
4+
-- .mod --
5+
module rsc.io/badmod
6+
hello world
7+
-- .info --
8+
{"Version":"v1.0.0"}
9+
-- x.go --
10+
package x
11+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
# Unknown lines should be ignored in dependency go.mod files.
2+
env GO111MODULE=on
3+
go list -m all
4+
5+
# ... and in replaced dependency go.mod files.
6+
cp go.mod go.mod.usesub
7+
go list -m all
8+
9+
# ... but not in the main module.
10+
cp go.mod.bad go.mod
11+
! go list -m all
12+
stderr 'unknown directive: hello'
13+
14+
-- go.mod --
15+
module m
16+
require rsc.io/badmod v1.0.0
17+
-- go.mod.bad --
18+
module m
19+
hello world
20+
-- go.mod.usesub --
21+
module m
22+
require rsc.io/badmod v1.0.0
23+
replace rsc.io/badmod v1.0.0 => ./sub
24+
-- sub/go.mod --
25+
module sub
26+
hello world

0 commit comments

Comments
 (0)