in modfile/rule.go [302:460]
func (f *File) add(errs *ErrorList, block *LineBlock, line *Line, verb string, args []string, fix VersionFixer, strict bool) {
// If strict is false, this module is a dependency.
// We ignore all unknown directives as well as main-module-only
// directives like replace and exclude. It will work better for
// forward compatibility if we can depend on modules that have unknown
// statements (presumed relevant only when acting as the main module)
// and simply ignore those statements.
if !strict {
switch verb {
case "go", "module", "retract", "require":
// want these even for dependency go.mods
default:
return
}
}
wrapModPathError := func(modPath string, err error) {
*errs = append(*errs, Error{
Filename: f.Syntax.Name,
Pos: line.Start,
ModPath: modPath,
Verb: verb,
Err: err,
})
}
wrapError := func(err error) {
*errs = append(*errs, Error{
Filename: f.Syntax.Name,
Pos: line.Start,
Err: err,
})
}
errorf := func(format string, args ...interface{}) {
wrapError(fmt.Errorf(format, args...))
}
switch verb {
default:
errorf("unknown directive: %s", verb)
case "go":
if f.Go != nil {
errorf("repeated go statement")
return
}
if len(args) != 1 {
errorf("go directive expects exactly one argument")
return
} else if !GoVersionRE.MatchString(args[0]) {
fixed := false
if !strict {
if m := laxGoVersionRE.FindStringSubmatch(args[0]); m != nil {
args[0] = m[1]
fixed = true
}
}
if !fixed {
errorf("invalid go version '%s': must match format 1.23", args[0])
return
}
}
f.Go = &Go{Syntax: line}
f.Go.Version = args[0]
case "module":
if f.Module != nil {
errorf("repeated module statement")
return
}
deprecated := parseDeprecation(block, line)
f.Module = &Module{
Syntax: line,
Deprecated: deprecated,
}
if len(args) != 1 {
errorf("usage: module module/path")
return
}
s, err := parseString(&args[0])
if err != nil {
errorf("invalid quoted string: %v", err)
return
}
f.Module.Mod = module.Version{Path: s}
case "require", "exclude":
if len(args) != 2 {
errorf("usage: %s module/path v1.2.3", verb)
return
}
s, err := parseString(&args[0])
if err != nil {
errorf("invalid quoted string: %v", err)
return
}
v, err := parseVersion(verb, s, &args[1], fix)
if err != nil {
wrapError(err)
return
}
pathMajor, err := modulePathMajor(s)
if err != nil {
wrapError(err)
return
}
if err := module.CheckPathMajor(v, pathMajor); err != nil {
wrapModPathError(s, err)
return
}
if verb == "require" {
f.Require = append(f.Require, &Require{
Mod: module.Version{Path: s, Version: v},
Syntax: line,
Indirect: isIndirect(line),
})
} else {
f.Exclude = append(f.Exclude, &Exclude{
Mod: module.Version{Path: s, Version: v},
Syntax: line,
})
}
case "replace":
replace, wrappederr := parseReplace(f.Syntax.Name, line, verb, args, fix)
if wrappederr != nil {
*errs = append(*errs, *wrappederr)
return
}
f.Replace = append(f.Replace, replace)
case "retract":
rationale := parseDirectiveComment(block, line)
vi, err := parseVersionInterval(verb, "", &args, dontFixRetract)
if err != nil {
if strict {
wrapError(err)
return
} else {
// Only report errors parsing intervals in the main module. We may
// support additional syntax in the future, such as open and half-open
// intervals. Those can't be supported now, because they break the
// go.mod parser, even in lax mode.
return
}
}
if len(args) > 0 && strict {
// In the future, there may be additional information after the version.
errorf("unexpected token after version: %q", args[0])
return
}
retract := &Retract{
VersionInterval: vi,
Rationale: rationale,
Syntax: line,
}
f.Retract = append(f.Retract, retract)
}
}