Source file src/cmd/vendor/golang.org/x/mod/modfile/rule.go

     1  // Copyright 2018 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  // Package modfile implements a parser and formatter for go.mod files.
     6  //
     7  // The go.mod syntax is described in
     8  // https://pkg.go.dev/cmd/go/#hdr-The_go_mod_file.
     9  //
    10  // The [Parse] and [ParseLax] functions both parse a go.mod file and return an
    11  // abstract syntax tree. ParseLax ignores unknown statements and may be used to
    12  // parse go.mod files that may have been developed with newer versions of Go.
    13  //
    14  // The [File] struct returned by Parse and ParseLax represent an abstract
    15  // go.mod file. File has several methods like [File.AddNewRequire] and
    16  // [File.DropReplace] that can be used to programmatically edit a file.
    17  //
    18  // The [Format] function formats a File back to a byte slice which can be
    19  // written to a file.
    20  package modfile
    21  
    22  import (
    23  	"errors"
    24  	"fmt"
    25  	"path/filepath"
    26  	"sort"
    27  	"strconv"
    28  	"strings"
    29  	"unicode"
    30  
    31  	"golang.org/x/mod/internal/lazyregexp"
    32  	"golang.org/x/mod/module"
    33  	"golang.org/x/mod/semver"
    34  )
    35  
    36  // A File is the parsed, interpreted form of a go.mod file.
    37  type File struct {
    38  	Module    *Module
    39  	Go        *Go
    40  	Toolchain *Toolchain
    41  	Godebug   []*Godebug
    42  	Require   []*Require
    43  	Exclude   []*Exclude
    44  	Replace   []*Replace
    45  	Retract   []*Retract
    46  	Tool      []*Tool
    47  
    48  	Syntax *FileSyntax
    49  }
    50  
    51  // A Module is the module statement.
    52  type Module struct {
    53  	Mod        module.Version
    54  	Deprecated string
    55  	Syntax     *Line
    56  }
    57  
    58  // A Go is the go statement.
    59  type Go struct {
    60  	Version string // "1.23"
    61  	Syntax  *Line
    62  }
    63  
    64  // A Toolchain is the toolchain statement.
    65  type Toolchain struct {
    66  	Name   string // "go1.21rc1"
    67  	Syntax *Line
    68  }
    69  
    70  // A Godebug is a single godebug key=value statement.
    71  type Godebug struct {
    72  	Key    string
    73  	Value  string
    74  	Syntax *Line
    75  }
    76  
    77  // An Exclude is a single exclude statement.
    78  type Exclude struct {
    79  	Mod    module.Version
    80  	Syntax *Line
    81  }
    82  
    83  // A Replace is a single replace statement.
    84  type Replace struct {
    85  	Old    module.Version
    86  	New    module.Version
    87  	Syntax *Line
    88  }
    89  
    90  // A Retract is a single retract statement.
    91  type Retract struct {
    92  	VersionInterval
    93  	Rationale string
    94  	Syntax    *Line
    95  }
    96  
    97  // A Tool is a single tool statement.
    98  type Tool struct {
    99  	Path   string
   100  	Syntax *Line
   101  }
   102  
   103  // A VersionInterval represents a range of versions with upper and lower bounds.
   104  // Intervals are closed: both bounds are included. When Low is equal to High,
   105  // the interval may refer to a single version ('v1.2.3') or an interval
   106  // ('[v1.2.3, v1.2.3]'); both have the same representation.
   107  type VersionInterval struct {
   108  	Low, High string
   109  }
   110  
   111  // A Require is a single require statement.
   112  type Require struct {
   113  	Mod      module.Version
   114  	Indirect bool // has "// indirect" comment
   115  	Syntax   *Line
   116  }
   117  
   118  func (r *Require) markRemoved() {
   119  	r.Syntax.markRemoved()
   120  	*r = Require{}
   121  }
   122  
   123  func (r *Require) setVersion(v string) {
   124  	r.Mod.Version = v
   125  
   126  	if line := r.Syntax; len(line.Token) > 0 {
   127  		if line.InBlock {
   128  			// If the line is preceded by an empty line, remove it; see
   129  			// https://golang.org/issue/33779.
   130  			if len(line.Comments.Before) == 1 && len(line.Comments.Before[0].Token) == 0 {
   131  				line.Comments.Before = line.Comments.Before[:0]
   132  			}
   133  			if len(line.Token) >= 2 { // example.com v1.2.3
   134  				line.Token[1] = v
   135  			}
   136  		} else {
   137  			if len(line.Token) >= 3 { // require example.com v1.2.3
   138  				line.Token[2] = v
   139  			}
   140  		}
   141  	}
   142  }
   143  
   144  // setIndirect sets line to have (or not have) a "// indirect" comment.
   145  func (r *Require) setIndirect(indirect bool) {
   146  	r.Indirect = indirect
   147  	line := r.Syntax
   148  	if isIndirect(line) == indirect {
   149  		return
   150  	}
   151  	if indirect {
   152  		// Adding comment.
   153  		if len(line.Suffix) == 0 {
   154  			// New comment.
   155  			line.Suffix = []Comment{{Token: "// indirect", Suffix: true}}
   156  			return
   157  		}
   158  
   159  		com := &line.Suffix[0]
   160  		text := strings.TrimSpace(strings.TrimPrefix(com.Token, string(slashSlash)))
   161  		if text == "" {
   162  			// Empty comment.
   163  			com.Token = "// indirect"
   164  			return
   165  		}
   166  
   167  		// Insert at beginning of existing comment.
   168  		com.Token = "// indirect; " + text
   169  		return
   170  	}
   171  
   172  	// Removing comment.
   173  	f := strings.TrimSpace(strings.TrimPrefix(line.Suffix[0].Token, string(slashSlash)))
   174  	if f == "indirect" {
   175  		// Remove whole comment.
   176  		line.Suffix = nil
   177  		return
   178  	}
   179  
   180  	// Remove comment prefix.
   181  	com := &line.Suffix[0]
   182  	i := strings.Index(com.Token, "indirect;")
   183  	com.Token = "//" + com.Token[i+len("indirect;"):]
   184  }
   185  
   186  // isIndirect reports whether line has a "// indirect" comment,
   187  // meaning it is in go.mod only for its effect on indirect dependencies,
   188  // so that it can be dropped entirely once the effective version of the
   189  // indirect dependency reaches the given minimum version.
   190  func isIndirect(line *Line) bool {
   191  	if len(line.Suffix) == 0 {
   192  		return false
   193  	}
   194  	f := strings.Fields(strings.TrimPrefix(line.Suffix[0].Token, string(slashSlash)))
   195  	return (len(f) == 1 && f[0] == "indirect" || len(f) > 1 && f[0] == "indirect;")
   196  }
   197  
   198  func (f *File) AddModuleStmt(path string) error {
   199  	if f.Syntax == nil {
   200  		f.Syntax = new(FileSyntax)
   201  	}
   202  	if f.Module == nil {
   203  		f.Module = &Module{
   204  			Mod:    module.Version{Path: path},
   205  			Syntax: f.Syntax.addLine(nil, "module", AutoQuote(path)),
   206  		}
   207  	} else {
   208  		f.Module.Mod.Path = path
   209  		f.Syntax.updateLine(f.Module.Syntax, "module", AutoQuote(path))
   210  	}
   211  	return nil
   212  }
   213  
   214  func (f *File) AddComment(text string) {
   215  	if f.Syntax == nil {
   216  		f.Syntax = new(FileSyntax)
   217  	}
   218  	f.Syntax.Stmt = append(f.Syntax.Stmt, &CommentBlock{
   219  		Comments: Comments{
   220  			Before: []Comment{
   221  				{
   222  					Token: text,
   223  				},
   224  			},
   225  		},
   226  	})
   227  }
   228  
   229  type VersionFixer func(path, version string) (string, error)
   230  
   231  // errDontFix is returned by a VersionFixer to indicate the version should be
   232  // left alone, even if it's not canonical.
   233  var dontFixRetract VersionFixer = func(_, vers string) (string, error) {
   234  	return vers, nil
   235  }
   236  
   237  // Parse parses and returns a go.mod file.
   238  //
   239  // file is the name of the file, used in positions and errors.
   240  //
   241  // data is the content of the file.
   242  //
   243  // fix is an optional function that canonicalizes module versions.
   244  // If fix is nil, all module versions must be canonical ([module.CanonicalVersion]
   245  // must return the same string).
   246  func Parse(file string, data []byte, fix VersionFixer) (*File, error) {
   247  	return parseToFile(file, data, fix, true)
   248  }
   249  
   250  // ParseLax is like Parse but ignores unknown statements.
   251  // It is used when parsing go.mod files other than the main module,
   252  // under the theory that most statement types we add in the future will
   253  // only apply in the main module, like exclude and replace,
   254  // and so we get better gradual deployments if old go commands
   255  // simply ignore those statements when found in go.mod files
   256  // in dependencies.
   257  func ParseLax(file string, data []byte, fix VersionFixer) (*File, error) {
   258  	return parseToFile(file, data, fix, false)
   259  }
   260  
   261  func parseToFile(file string, data []byte, fix VersionFixer, strict bool) (parsed *File, err error) {
   262  	fs, err := parse(file, data)
   263  	if err != nil {
   264  		return nil, err
   265  	}
   266  	f := &File{
   267  		Syntax: fs,
   268  	}
   269  	var errs ErrorList
   270  
   271  	// fix versions in retract directives after the file is parsed.
   272  	// We need the module path to fix versions, and it might be at the end.
   273  	defer func() {
   274  		oldLen := len(errs)
   275  		f.fixRetract(fix, &errs)
   276  		if len(errs) > oldLen {
   277  			parsed, err = nil, errs
   278  		}
   279  	}()
   280  
   281  	for _, x := range fs.Stmt {
   282  		switch x := x.(type) {
   283  		case *Line:
   284  			f.add(&errs, nil, x, x.Token[0], x.Token[1:], fix, strict)
   285  
   286  		case *LineBlock:
   287  			if len(x.Token) > 1 {
   288  				if strict {
   289  					errs = append(errs, Error{
   290  						Filename: file,
   291  						Pos:      x.Start,
   292  						Err:      fmt.Errorf("unknown block type: %s", strings.Join(x.Token, " ")),
   293  					})
   294  				}
   295  				continue
   296  			}
   297  			switch x.Token[0] {
   298  			default:
   299  				if strict {
   300  					errs = append(errs, Error{
   301  						Filename: file,
   302  						Pos:      x.Start,
   303  						Err:      fmt.Errorf("unknown block type: %s", strings.Join(x.Token, " ")),
   304  					})
   305  				}
   306  				continue
   307  			case "module", "godebug", "require", "exclude", "replace", "retract", "tool":
   308  				for _, l := range x.Line {
   309  					f.add(&errs, x, l, x.Token[0], l.Token, fix, strict)
   310  				}
   311  			}
   312  		}
   313  	}
   314  
   315  	if len(errs) > 0 {
   316  		return nil, errs
   317  	}
   318  	return f, nil
   319  }
   320  
   321  var GoVersionRE = lazyregexp.New(`^([1-9][0-9]*)\.(0|[1-9][0-9]*)(\.(0|[1-9][0-9]*))?([a-z]+[0-9]+)?$`)
   322  var laxGoVersionRE = lazyregexp.New(`^v?(([1-9][0-9]*)\.(0|[1-9][0-9]*))([^0-9].*)$`)
   323  
   324  // Toolchains must be named beginning with `go1`,
   325  // like "go1.20.3" or "go1.20.3-gccgo". As a special case, "default" is also permitted.
   326  // Note that this regexp is a much looser condition than go/version.IsValid,
   327  // for forward compatibility.
   328  // (This code has to be work to identify new toolchains even if we tweak the syntax in the future.)
   329  var ToolchainRE = lazyregexp.New(`^default$|^go1($|\.)`)
   330  
   331  func (f *File) add(errs *ErrorList, block *LineBlock, line *Line, verb string, args []string, fix VersionFixer, strict bool) {
   332  	// If strict is false, this module is a dependency.
   333  	// We ignore all unknown directives as well as main-module-only
   334  	// directives like replace and exclude. It will work better for
   335  	// forward compatibility if we can depend on modules that have unknown
   336  	// statements (presumed relevant only when acting as the main module)
   337  	// and simply ignore those statements.
   338  	if !strict {
   339  		switch verb {
   340  		case "go", "module", "retract", "require":
   341  			// want these even for dependency go.mods
   342  		default:
   343  			return
   344  		}
   345  	}
   346  
   347  	wrapModPathError := func(modPath string, err error) {
   348  		*errs = append(*errs, Error{
   349  			Filename: f.Syntax.Name,
   350  			Pos:      line.Start,
   351  			ModPath:  modPath,
   352  			Verb:     verb,
   353  			Err:      err,
   354  		})
   355  	}
   356  	wrapError := func(err error) {
   357  		*errs = append(*errs, Error{
   358  			Filename: f.Syntax.Name,
   359  			Pos:      line.Start,
   360  			Err:      err,
   361  		})
   362  	}
   363  	errorf := func(format string, args ...interface{}) {
   364  		wrapError(fmt.Errorf(format, args...))
   365  	}
   366  
   367  	switch verb {
   368  	default:
   369  		errorf("unknown directive: %s", verb)
   370  
   371  	case "go":
   372  		if f.Go != nil {
   373  			errorf("repeated go statement")
   374  			return
   375  		}
   376  		if len(args) != 1 {
   377  			errorf("go directive expects exactly one argument")
   378  			return
   379  		} else if !GoVersionRE.MatchString(args[0]) {
   380  			fixed := false
   381  			if !strict {
   382  				if m := laxGoVersionRE.FindStringSubmatch(args[0]); m != nil {
   383  					args[0] = m[1]
   384  					fixed = true
   385  				}
   386  			}
   387  			if !fixed {
   388  				errorf("invalid go version '%s': must match format 1.23.0", args[0])
   389  				return
   390  			}
   391  		}
   392  
   393  		f.Go = &Go{Syntax: line}
   394  		f.Go.Version = args[0]
   395  
   396  	case "toolchain":
   397  		if f.Toolchain != nil {
   398  			errorf("repeated toolchain statement")
   399  			return
   400  		}
   401  		if len(args) != 1 {
   402  			errorf("toolchain directive expects exactly one argument")
   403  			return
   404  		} else if !ToolchainRE.MatchString(args[0]) {
   405  			errorf("invalid toolchain version '%s': must match format go1.23.0 or default", args[0])
   406  			return
   407  		}
   408  		f.Toolchain = &Toolchain{Syntax: line}
   409  		f.Toolchain.Name = args[0]
   410  
   411  	case "module":
   412  		if f.Module != nil {
   413  			errorf("repeated module statement")
   414  			return
   415  		}
   416  		deprecated := parseDeprecation(block, line)
   417  		f.Module = &Module{
   418  			Syntax:     line,
   419  			Deprecated: deprecated,
   420  		}
   421  		if len(args) != 1 {
   422  			errorf("usage: module module/path")
   423  			return
   424  		}
   425  		s, err := parseString(&args[0])
   426  		if err != nil {
   427  			errorf("invalid quoted string: %v", err)
   428  			return
   429  		}
   430  		f.Module.Mod = module.Version{Path: s}
   431  
   432  	case "godebug":
   433  		if len(args) != 1 || strings.ContainsAny(args[0], "\"`',") {
   434  			errorf("usage: godebug key=value")
   435  			return
   436  		}
   437  		key, value, ok := strings.Cut(args[0], "=")
   438  		if !ok {
   439  			errorf("usage: godebug key=value")
   440  			return
   441  		}
   442  		f.Godebug = append(f.Godebug, &Godebug{
   443  			Key:    key,
   444  			Value:  value,
   445  			Syntax: line,
   446  		})
   447  
   448  	case "require", "exclude":
   449  		if len(args) != 2 {
   450  			errorf("usage: %s module/path v1.2.3", verb)
   451  			return
   452  		}
   453  		s, err := parseString(&args[0])
   454  		if err != nil {
   455  			errorf("invalid quoted string: %v", err)
   456  			return
   457  		}
   458  		v, err := parseVersion(verb, s, &args[1], fix)
   459  		if err != nil {
   460  			wrapError(err)
   461  			return
   462  		}
   463  		pathMajor, err := modulePathMajor(s)
   464  		if err != nil {
   465  			wrapError(err)
   466  			return
   467  		}
   468  		if err := module.CheckPathMajor(v, pathMajor); err != nil {
   469  			wrapModPathError(s, err)
   470  			return
   471  		}
   472  		if verb == "require" {
   473  			f.Require = append(f.Require, &Require{
   474  				Mod:      module.Version{Path: s, Version: v},
   475  				Syntax:   line,
   476  				Indirect: isIndirect(line),
   477  			})
   478  		} else {
   479  			f.Exclude = append(f.Exclude, &Exclude{
   480  				Mod:    module.Version{Path: s, Version: v},
   481  				Syntax: line,
   482  			})
   483  		}
   484  
   485  	case "replace":
   486  		replace, wrappederr := parseReplace(f.Syntax.Name, line, verb, args, fix)
   487  		if wrappederr != nil {
   488  			*errs = append(*errs, *wrappederr)
   489  			return
   490  		}
   491  		f.Replace = append(f.Replace, replace)
   492  
   493  	case "retract":
   494  		rationale := parseDirectiveComment(block, line)
   495  		vi, err := parseVersionInterval(verb, "", &args, dontFixRetract)
   496  		if err != nil {
   497  			if strict {
   498  				wrapError(err)
   499  				return
   500  			} else {
   501  				// Only report errors parsing intervals in the main module. We may
   502  				// support additional syntax in the future, such as open and half-open
   503  				// intervals. Those can't be supported now, because they break the
   504  				// go.mod parser, even in lax mode.
   505  				return
   506  			}
   507  		}
   508  		if len(args) > 0 && strict {
   509  			// In the future, there may be additional information after the version.
   510  			errorf("unexpected token after version: %q", args[0])
   511  			return
   512  		}
   513  		retract := &Retract{
   514  			VersionInterval: vi,
   515  			Rationale:       rationale,
   516  			Syntax:          line,
   517  		}
   518  		f.Retract = append(f.Retract, retract)
   519  
   520  	case "tool":
   521  		if len(args) != 1 {
   522  			errorf("tool directive expects exactly one argument")
   523  			return
   524  		}
   525  		s, err := parseString(&args[0])
   526  		if err != nil {
   527  			errorf("invalid quoted string: %v", err)
   528  			return
   529  		}
   530  		f.Tool = append(f.Tool, &Tool{
   531  			Path:   s,
   532  			Syntax: line,
   533  		})
   534  	}
   535  }
   536  
   537  func parseReplace(filename string, line *Line, verb string, args []string, fix VersionFixer) (*Replace, *Error) {
   538  	wrapModPathError := func(modPath string, err error) *Error {
   539  		return &Error{
   540  			Filename: filename,
   541  			Pos:      line.Start,
   542  			ModPath:  modPath,
   543  			Verb:     verb,
   544  			Err:      err,
   545  		}
   546  	}
   547  	wrapError := func(err error) *Error {
   548  		return &Error{
   549  			Filename: filename,
   550  			Pos:      line.Start,
   551  			Err:      err,
   552  		}
   553  	}
   554  	errorf := func(format string, args ...interface{}) *Error {
   555  		return wrapError(fmt.Errorf(format, args...))
   556  	}
   557  
   558  	arrow := 2
   559  	if len(args) >= 2 && args[1] == "=>" {
   560  		arrow = 1
   561  	}
   562  	if len(args) < arrow+2 || len(args) > arrow+3 || args[arrow] != "=>" {
   563  		return nil, errorf("usage: %s module/path [v1.2.3] => other/module v1.4\n\t or %s module/path [v1.2.3] => ../local/directory", verb, verb)
   564  	}
   565  	s, err := parseString(&args[0])
   566  	if err != nil {
   567  		return nil, errorf("invalid quoted string: %v", err)
   568  	}
   569  	pathMajor, err := modulePathMajor(s)
   570  	if err != nil {
   571  		return nil, wrapModPathError(s, err)
   572  
   573  	}
   574  	var v string
   575  	if arrow == 2 {
   576  		v, err = parseVersion(verb, s, &args[1], fix)
   577  		if err != nil {
   578  			return nil, wrapError(err)
   579  		}
   580  		if err := module.CheckPathMajor(v, pathMajor); err != nil {
   581  			return nil, wrapModPathError(s, err)
   582  		}
   583  	}
   584  	ns, err := parseString(&args[arrow+1])
   585  	if err != nil {
   586  		return nil, errorf("invalid quoted string: %v", err)
   587  	}
   588  	nv := ""
   589  	if len(args) == arrow+2 {
   590  		if !IsDirectoryPath(ns) {
   591  			if strings.Contains(ns, "@") {
   592  				return nil, errorf("replacement module must match format 'path version', not 'path@version'")
   593  			}
   594  			return nil, errorf("replacement module without version must be directory path (rooted or starting with . or ..)")
   595  		}
   596  		if filepath.Separator == '/' && strings.Contains(ns, `\`) {
   597  			return nil, errorf("replacement directory appears to be Windows path (on a non-windows system)")
   598  		}
   599  	}
   600  	if len(args) == arrow+3 {
   601  		nv, err = parseVersion(verb, ns, &args[arrow+2], fix)
   602  		if err != nil {
   603  			return nil, wrapError(err)
   604  		}
   605  		if IsDirectoryPath(ns) {
   606  			return nil, errorf("replacement module directory path %q cannot have version", ns)
   607  		}
   608  	}
   609  	return &Replace{
   610  		Old:    module.Version{Path: s, Version: v},
   611  		New:    module.Version{Path: ns, Version: nv},
   612  		Syntax: line,
   613  	}, nil
   614  }
   615  
   616  // fixRetract applies fix to each retract directive in f, appending any errors
   617  // to errs.
   618  //
   619  // Most versions are fixed as we parse the file, but for retract directives,
   620  // the relevant module path is the one specified with the module directive,
   621  // and that might appear at the end of the file (or not at all).
   622  func (f *File) fixRetract(fix VersionFixer, errs *ErrorList) {
   623  	if fix == nil {
   624  		return
   625  	}
   626  	path := ""
   627  	if f.Module != nil {
   628  		path = f.Module.Mod.Path
   629  	}
   630  	var r *Retract
   631  	wrapError := func(err error) {
   632  		*errs = append(*errs, Error{
   633  			Filename: f.Syntax.Name,
   634  			Pos:      r.Syntax.Start,
   635  			Err:      err,
   636  		})
   637  	}
   638  
   639  	for _, r = range f.Retract {
   640  		if path == "" {
   641  			wrapError(errors.New("no module directive found, so retract cannot be used"))
   642  			return // only print the first one of these
   643  		}
   644  
   645  		args := r.Syntax.Token
   646  		if args[0] == "retract" {
   647  			args = args[1:]
   648  		}
   649  		vi, err := parseVersionInterval("retract", path, &args, fix)
   650  		if err != nil {
   651  			wrapError(err)
   652  		}
   653  		r.VersionInterval = vi
   654  	}
   655  }
   656  
   657  func (f *WorkFile) add(errs *ErrorList, line *Line, verb string, args []string, fix VersionFixer) {
   658  	wrapError := func(err error) {
   659  		*errs = append(*errs, Error{
   660  			Filename: f.Syntax.Name,
   661  			Pos:      line.Start,
   662  			Err:      err,
   663  		})
   664  	}
   665  	errorf := func(format string, args ...interface{}) {
   666  		wrapError(fmt.Errorf(format, args...))
   667  	}
   668  
   669  	switch verb {
   670  	default:
   671  		errorf("unknown directive: %s", verb)
   672  
   673  	case "go":
   674  		if f.Go != nil {
   675  			errorf("repeated go statement")
   676  			return
   677  		}
   678  		if len(args) != 1 {
   679  			errorf("go directive expects exactly one argument")
   680  			return
   681  		} else if !GoVersionRE.MatchString(args[0]) {
   682  			errorf("invalid go version '%s': must match format 1.23.0", args[0])
   683  			return
   684  		}
   685  
   686  		f.Go = &Go{Syntax: line}
   687  		f.Go.Version = args[0]
   688  
   689  	case "toolchain":
   690  		if f.Toolchain != nil {
   691  			errorf("repeated toolchain statement")
   692  			return
   693  		}
   694  		if len(args) != 1 {
   695  			errorf("toolchain directive expects exactly one argument")
   696  			return
   697  		} else if !ToolchainRE.MatchString(args[0]) {
   698  			errorf("invalid toolchain version '%s': must match format go1.23.0 or default", args[0])
   699  			return
   700  		}
   701  
   702  		f.Toolchain = &Toolchain{Syntax: line}
   703  		f.Toolchain.Name = args[0]
   704  
   705  	case "godebug":
   706  		if len(args) != 1 || strings.ContainsAny(args[0], "\"`',") {
   707  			errorf("usage: godebug key=value")
   708  			return
   709  		}
   710  		key, value, ok := strings.Cut(args[0], "=")
   711  		if !ok {
   712  			errorf("usage: godebug key=value")
   713  			return
   714  		}
   715  		f.Godebug = append(f.Godebug, &Godebug{
   716  			Key:    key,
   717  			Value:  value,
   718  			Syntax: line,
   719  		})
   720  
   721  	case "use":
   722  		if len(args) != 1 {
   723  			errorf("usage: %s local/dir", verb)
   724  			return
   725  		}
   726  		s, err := parseString(&args[0])
   727  		if err != nil {
   728  			errorf("invalid quoted string: %v", err)
   729  			return
   730  		}
   731  		f.Use = append(f.Use, &Use{
   732  			Path:   s,
   733  			Syntax: line,
   734  		})
   735  
   736  	case "replace":
   737  		replace, wrappederr := parseReplace(f.Syntax.Name, line, verb, args, fix)
   738  		if wrappederr != nil {
   739  			*errs = append(*errs, *wrappederr)
   740  			return
   741  		}
   742  		f.Replace = append(f.Replace, replace)
   743  	}
   744  }
   745  
   746  // IsDirectoryPath reports whether the given path should be interpreted as a directory path.
   747  // Just like on the go command line, relative paths starting with a '.' or '..' path component
   748  // and rooted paths are directory paths; the rest are module paths.
   749  func IsDirectoryPath(ns string) bool {
   750  	// Because go.mod files can move from one system to another,
   751  	// we check all known path syntaxes, both Unix and Windows.
   752  	return ns == "." || strings.HasPrefix(ns, "./") || strings.HasPrefix(ns, `.\`) ||
   753  		ns == ".." || strings.HasPrefix(ns, "../") || strings.HasPrefix(ns, `..\`) ||
   754  		strings.HasPrefix(ns, "/") || strings.HasPrefix(ns, `\`) ||
   755  		len(ns) >= 2 && ('A' <= ns[0] && ns[0] <= 'Z' || 'a' <= ns[0] && ns[0] <= 'z') && ns[1] == ':'
   756  }
   757  
   758  // MustQuote reports whether s must be quoted in order to appear as
   759  // a single token in a go.mod line.
   760  func MustQuote(s string) bool {
   761  	for _, r := range s {
   762  		switch r {
   763  		case ' ', '"', '\'', '`':
   764  			return true
   765  
   766  		case '(', ')', '[', ']', '{', '}', ',':
   767  			if len(s) > 1 {
   768  				return true
   769  			}
   770  
   771  		default:
   772  			if !unicode.IsPrint(r) {
   773  				return true
   774  			}
   775  		}
   776  	}
   777  	return s == "" || strings.Contains(s, "//") || strings.Contains(s, "/*")
   778  }
   779  
   780  // AutoQuote returns s or, if quoting is required for s to appear in a go.mod,
   781  // the quotation of s.
   782  func AutoQuote(s string) string {
   783  	if MustQuote(s) {
   784  		return strconv.Quote(s)
   785  	}
   786  	return s
   787  }
   788  
   789  func parseVersionInterval(verb string, path string, args *[]string, fix VersionFixer) (VersionInterval, error) {
   790  	toks := *args
   791  	if len(toks) == 0 || toks[0] == "(" {
   792  		return VersionInterval{}, fmt.Errorf("expected '[' or version")
   793  	}
   794  	if toks[0] != "[" {
   795  		v, err := parseVersion(verb, path, &toks[0], fix)
   796  		if err != nil {
   797  			return VersionInterval{}, err
   798  		}
   799  		*args = toks[1:]
   800  		return VersionInterval{Low: v, High: v}, nil
   801  	}
   802  	toks = toks[1:]
   803  
   804  	if len(toks) == 0 {
   805  		return VersionInterval{}, fmt.Errorf("expected version after '['")
   806  	}
   807  	low, err := parseVersion(verb, path, &toks[0], fix)
   808  	if err != nil {
   809  		return VersionInterval{}, err
   810  	}
   811  	toks = toks[1:]
   812  
   813  	if len(toks) == 0 || toks[0] != "," {
   814  		return VersionInterval{}, fmt.Errorf("expected ',' after version")
   815  	}
   816  	toks = toks[1:]
   817  
   818  	if len(toks) == 0 {
   819  		return VersionInterval{}, fmt.Errorf("expected version after ','")
   820  	}
   821  	high, err := parseVersion(verb, path, &toks[0], fix)
   822  	if err != nil {
   823  		return VersionInterval{}, err
   824  	}
   825  	toks = toks[1:]
   826  
   827  	if len(toks) == 0 || toks[0] != "]" {
   828  		return VersionInterval{}, fmt.Errorf("expected ']' after version")
   829  	}
   830  	toks = toks[1:]
   831  
   832  	*args = toks
   833  	return VersionInterval{Low: low, High: high}, nil
   834  }
   835  
   836  func parseString(s *string) (string, error) {
   837  	t := *s
   838  	if strings.HasPrefix(t, `"`) {
   839  		var err error
   840  		if t, err = strconv.Unquote(t); err != nil {
   841  			return "", err
   842  		}
   843  	} else if strings.ContainsAny(t, "\"'`") {
   844  		// Other quotes are reserved both for possible future expansion
   845  		// and to avoid confusion. For example if someone types 'x'
   846  		// we want that to be a syntax error and not a literal x in literal quotation marks.
   847  		return "", fmt.Errorf("unquoted string cannot contain quote")
   848  	}
   849  	*s = AutoQuote(t)
   850  	return t, nil
   851  }
   852  
   853  var deprecatedRE = lazyregexp.New(`(?s)(?:^|\n\n)Deprecated: *(.*?)(?:$|\n\n)`)
   854  
   855  // parseDeprecation extracts the text of comments on a "module" directive and
   856  // extracts a deprecation message from that.
   857  //
   858  // A deprecation message is contained in a paragraph within a block of comments
   859  // that starts with "Deprecated:" (case sensitive). The message runs until the
   860  // end of the paragraph and does not include the "Deprecated:" prefix. If the
   861  // comment block has multiple paragraphs that start with "Deprecated:",
   862  // parseDeprecation returns the message from the first.
   863  func parseDeprecation(block *LineBlock, line *Line) string {
   864  	text := parseDirectiveComment(block, line)
   865  	m := deprecatedRE.FindStringSubmatch(text)
   866  	if m == nil {
   867  		return ""
   868  	}
   869  	return m[1]
   870  }
   871  
   872  // parseDirectiveComment extracts the text of comments on a directive.
   873  // If the directive's line does not have comments and is part of a block that
   874  // does have comments, the block's comments are used.
   875  func parseDirectiveComment(block *LineBlock, line *Line) string {
   876  	comments := line.Comment()
   877  	if block != nil && len(comments.Before) == 0 && len(comments.Suffix) == 0 {
   878  		comments = block.Comment()
   879  	}
   880  	groups := [][]Comment{comments.Before, comments.Suffix}
   881  	var lines []string
   882  	for _, g := range groups {
   883  		for _, c := range g {
   884  			if !strings.HasPrefix(c.Token, "//") {
   885  				continue // blank line
   886  			}
   887  			lines = append(lines, strings.TrimSpace(strings.TrimPrefix(c.Token, "//")))
   888  		}
   889  	}
   890  	return strings.Join(lines, "\n")
   891  }
   892  
   893  type ErrorList []Error
   894  
   895  func (e ErrorList) Error() string {
   896  	errStrs := make([]string, len(e))
   897  	for i, err := range e {
   898  		errStrs[i] = err.Error()
   899  	}
   900  	return strings.Join(errStrs, "\n")
   901  }
   902  
   903  type Error struct {
   904  	Filename string
   905  	Pos      Position
   906  	Verb     string
   907  	ModPath  string
   908  	Err      error
   909  }
   910  
   911  func (e *Error) Error() string {
   912  	var pos string
   913  	if e.Pos.LineRune > 1 {
   914  		// Don't print LineRune if it's 1 (beginning of line).
   915  		// It's always 1 except in scanner errors, which are rare.
   916  		pos = fmt.Sprintf("%s:%d:%d: ", e.Filename, e.Pos.Line, e.Pos.LineRune)
   917  	} else if e.Pos.Line > 0 {
   918  		pos = fmt.Sprintf("%s:%d: ", e.Filename, e.Pos.Line)
   919  	} else if e.Filename != "" {
   920  		pos = fmt.Sprintf("%s: ", e.Filename)
   921  	}
   922  
   923  	var directive string
   924  	if e.ModPath != "" {
   925  		directive = fmt.Sprintf("%s %s: ", e.Verb, e.ModPath)
   926  	} else if e.Verb != "" {
   927  		directive = fmt.Sprintf("%s: ", e.Verb)
   928  	}
   929  
   930  	return pos + directive + e.Err.Error()
   931  }
   932  
   933  func (e *Error) Unwrap() error { return e.Err }
   934  
   935  func parseVersion(verb string, path string, s *string, fix VersionFixer) (string, error) {
   936  	t, err := parseString(s)
   937  	if err != nil {
   938  		return "", &Error{
   939  			Verb:    verb,
   940  			ModPath: path,
   941  			Err: &module.InvalidVersionError{
   942  				Version: *s,
   943  				Err:     err,
   944  			},
   945  		}
   946  	}
   947  	if fix != nil {
   948  		fixed, err := fix(path, t)
   949  		if err != nil {
   950  			if err, ok := err.(*module.ModuleError); ok {
   951  				return "", &Error{
   952  					Verb:    verb,
   953  					ModPath: path,
   954  					Err:     err.Err,
   955  				}
   956  			}
   957  			return "", err
   958  		}
   959  		t = fixed
   960  	} else {
   961  		cv := module.CanonicalVersion(t)
   962  		if cv == "" {
   963  			return "", &Error{
   964  				Verb:    verb,
   965  				ModPath: path,
   966  				Err: &module.InvalidVersionError{
   967  					Version: t,
   968  					Err:     errors.New("must be of the form v1.2.3"),
   969  				},
   970  			}
   971  		}
   972  		t = cv
   973  	}
   974  	*s = t
   975  	return *s, nil
   976  }
   977  
   978  func modulePathMajor(path string) (string, error) {
   979  	_, major, ok := module.SplitPathVersion(path)
   980  	if !ok {
   981  		return "", fmt.Errorf("invalid module path")
   982  	}
   983  	return major, nil
   984  }
   985  
   986  func (f *File) Format() ([]byte, error) {
   987  	return Format(f.Syntax), nil
   988  }
   989  
   990  // Cleanup cleans up the file f after any edit operations.
   991  // To avoid quadratic behavior, modifications like [File.DropRequire]
   992  // clear the entry but do not remove it from the slice.
   993  // Cleanup cleans out all the cleared entries.
   994  func (f *File) Cleanup() {
   995  	w := 0
   996  	for _, g := range f.Godebug {
   997  		if g.Key != "" {
   998  			f.Godebug[w] = g
   999  			w++
  1000  		}
  1001  	}
  1002  	f.Godebug = f.Godebug[:w]
  1003  
  1004  	w = 0
  1005  	for _, r := range f.Require {
  1006  		if r.Mod.Path != "" {
  1007  			f.Require[w] = r
  1008  			w++
  1009  		}
  1010  	}
  1011  	f.Require = f.Require[:w]
  1012  
  1013  	w = 0
  1014  	for _, x := range f.Exclude {
  1015  		if x.Mod.Path != "" {
  1016  			f.Exclude[w] = x
  1017  			w++
  1018  		}
  1019  	}
  1020  	f.Exclude = f.Exclude[:w]
  1021  
  1022  	w = 0
  1023  	for _, r := range f.Replace {
  1024  		if r.Old.Path != "" {
  1025  			f.Replace[w] = r
  1026  			w++
  1027  		}
  1028  	}
  1029  	f.Replace = f.Replace[:w]
  1030  
  1031  	w = 0
  1032  	for _, r := range f.Retract {
  1033  		if r.Low != "" || r.High != "" {
  1034  			f.Retract[w] = r
  1035  			w++
  1036  		}
  1037  	}
  1038  	f.Retract = f.Retract[:w]
  1039  
  1040  	f.Syntax.Cleanup()
  1041  }
  1042  
  1043  func (f *File) AddGoStmt(version string) error {
  1044  	if !GoVersionRE.MatchString(version) {
  1045  		return fmt.Errorf("invalid language version %q", version)
  1046  	}
  1047  	if f.Go == nil {
  1048  		var hint Expr
  1049  		if f.Module != nil && f.Module.Syntax != nil {
  1050  			hint = f.Module.Syntax
  1051  		} else if f.Syntax == nil {
  1052  			f.Syntax = new(FileSyntax)
  1053  		}
  1054  		f.Go = &Go{
  1055  			Version: version,
  1056  			Syntax:  f.Syntax.addLine(hint, "go", version),
  1057  		}
  1058  	} else {
  1059  		f.Go.Version = version
  1060  		f.Syntax.updateLine(f.Go.Syntax, "go", version)
  1061  	}
  1062  	return nil
  1063  }
  1064  
  1065  // DropGoStmt deletes the go statement from the file.
  1066  func (f *File) DropGoStmt() {
  1067  	if f.Go != nil {
  1068  		f.Go.Syntax.markRemoved()
  1069  		f.Go = nil
  1070  	}
  1071  }
  1072  
  1073  // DropToolchainStmt deletes the toolchain statement from the file.
  1074  func (f *File) DropToolchainStmt() {
  1075  	if f.Toolchain != nil {
  1076  		f.Toolchain.Syntax.markRemoved()
  1077  		f.Toolchain = nil
  1078  	}
  1079  }
  1080  
  1081  func (f *File) AddToolchainStmt(name string) error {
  1082  	if !ToolchainRE.MatchString(name) {
  1083  		return fmt.Errorf("invalid toolchain name %q", name)
  1084  	}
  1085  	if f.Toolchain == nil {
  1086  		var hint Expr
  1087  		if f.Go != nil && f.Go.Syntax != nil {
  1088  			hint = f.Go.Syntax
  1089  		} else if f.Module != nil && f.Module.Syntax != nil {
  1090  			hint = f.Module.Syntax
  1091  		}
  1092  		f.Toolchain = &Toolchain{
  1093  			Name:   name,
  1094  			Syntax: f.Syntax.addLine(hint, "toolchain", name),
  1095  		}
  1096  	} else {
  1097  		f.Toolchain.Name = name
  1098  		f.Syntax.updateLine(f.Toolchain.Syntax, "toolchain", name)
  1099  	}
  1100  	return nil
  1101  }
  1102  
  1103  // AddGodebug sets the first godebug line for key to value,
  1104  // preserving any existing comments for that line and removing all
  1105  // other godebug lines for key.
  1106  //
  1107  // If no line currently exists for key, AddGodebug adds a new line
  1108  // at the end of the last godebug block.
  1109  func (f *File) AddGodebug(key, value string) error {
  1110  	need := true
  1111  	for _, g := range f.Godebug {
  1112  		if g.Key == key {
  1113  			if need {
  1114  				g.Value = value
  1115  				f.Syntax.updateLine(g.Syntax, "godebug", key+"="+value)
  1116  				need = false
  1117  			} else {
  1118  				g.Syntax.markRemoved()
  1119  				*g = Godebug{}
  1120  			}
  1121  		}
  1122  	}
  1123  
  1124  	if need {
  1125  		f.addNewGodebug(key, value)
  1126  	}
  1127  	return nil
  1128  }
  1129  
  1130  // addNewGodebug adds a new godebug key=value line at the end
  1131  // of the last godebug block, regardless of any existing godebug lines for key.
  1132  func (f *File) addNewGodebug(key, value string) {
  1133  	line := f.Syntax.addLine(nil, "godebug", key+"="+value)
  1134  	g := &Godebug{
  1135  		Key:    key,
  1136  		Value:  value,
  1137  		Syntax: line,
  1138  	}
  1139  	f.Godebug = append(f.Godebug, g)
  1140  }
  1141  
  1142  // AddRequire sets the first require line for path to version vers,
  1143  // preserving any existing comments for that line and removing all
  1144  // other lines for path.
  1145  //
  1146  // If no line currently exists for path, AddRequire adds a new line
  1147  // at the end of the last require block.
  1148  func (f *File) AddRequire(path, vers string) error {
  1149  	need := true
  1150  	for _, r := range f.Require {
  1151  		if r.Mod.Path == path {
  1152  			if need {
  1153  				r.Mod.Version = vers
  1154  				f.Syntax.updateLine(r.Syntax, "require", AutoQuote(path), vers)
  1155  				need = false
  1156  			} else {
  1157  				r.Syntax.markRemoved()
  1158  				*r = Require{}
  1159  			}
  1160  		}
  1161  	}
  1162  
  1163  	if need {
  1164  		f.AddNewRequire(path, vers, false)
  1165  	}
  1166  	return nil
  1167  }
  1168  
  1169  // AddNewRequire adds a new require line for path at version vers at the end of
  1170  // the last require block, regardless of any existing require lines for path.
  1171  func (f *File) AddNewRequire(path, vers string, indirect bool) {
  1172  	line := f.Syntax.addLine(nil, "require", AutoQuote(path), vers)
  1173  	r := &Require{
  1174  		Mod:    module.Version{Path: path, Version: vers},
  1175  		Syntax: line,
  1176  	}
  1177  	r.setIndirect(indirect)
  1178  	f.Require = append(f.Require, r)
  1179  }
  1180  
  1181  // SetRequire updates the requirements of f to contain exactly req, preserving
  1182  // the existing block structure and line comment contents (except for 'indirect'
  1183  // markings) for the first requirement on each named module path.
  1184  //
  1185  // The Syntax field is ignored for the requirements in req.
  1186  //
  1187  // Any requirements not already present in the file are added to the block
  1188  // containing the last require line.
  1189  //
  1190  // The requirements in req must specify at most one distinct version for each
  1191  // module path.
  1192  //
  1193  // If any existing requirements may be removed, the caller should call
  1194  // [File.Cleanup] after all edits are complete.
  1195  func (f *File) SetRequire(req []*Require) {
  1196  	type elem struct {
  1197  		version  string
  1198  		indirect bool
  1199  	}
  1200  	need := make(map[string]elem)
  1201  	for _, r := range req {
  1202  		if prev, dup := need[r.Mod.Path]; dup && prev.version != r.Mod.Version {
  1203  			panic(fmt.Errorf("SetRequire called with conflicting versions for path %s (%s and %s)", r.Mod.Path, prev.version, r.Mod.Version))
  1204  		}
  1205  		need[r.Mod.Path] = elem{r.Mod.Version, r.Indirect}
  1206  	}
  1207  
  1208  	// Update or delete the existing Require entries to preserve
  1209  	// only the first for each module path in req.
  1210  	for _, r := range f.Require {
  1211  		e, ok := need[r.Mod.Path]
  1212  		if ok {
  1213  			r.setVersion(e.version)
  1214  			r.setIndirect(e.indirect)
  1215  		} else {
  1216  			r.markRemoved()
  1217  		}
  1218  		delete(need, r.Mod.Path)
  1219  	}
  1220  
  1221  	// Add new entries in the last block of the file for any paths that weren't
  1222  	// already present.
  1223  	//
  1224  	// This step is nondeterministic, but the final result will be deterministic
  1225  	// because we will sort the block.
  1226  	for path, e := range need {
  1227  		f.AddNewRequire(path, e.version, e.indirect)
  1228  	}
  1229  
  1230  	f.SortBlocks()
  1231  }
  1232  
  1233  // SetRequireSeparateIndirect updates the requirements of f to contain the given
  1234  // requirements. Comment contents (except for 'indirect' markings) are retained
  1235  // from the first existing requirement for each module path. Like SetRequire,
  1236  // SetRequireSeparateIndirect adds requirements for new paths in req,
  1237  // updates the version and "// indirect" comment on existing requirements,
  1238  // and deletes requirements on paths not in req. Existing duplicate requirements
  1239  // are deleted.
  1240  //
  1241  // As its name suggests, SetRequireSeparateIndirect puts direct and indirect
  1242  // requirements into two separate blocks, one containing only direct
  1243  // requirements, and the other containing only indirect requirements.
  1244  // SetRequireSeparateIndirect may move requirements between these two blocks
  1245  // when their indirect markings change. However, SetRequireSeparateIndirect
  1246  // won't move requirements from other blocks, especially blocks with comments.
  1247  //
  1248  // If the file initially has one uncommented block of requirements,
  1249  // SetRequireSeparateIndirect will split it into a direct-only and indirect-only
  1250  // block. This aids in the transition to separate blocks.
  1251  func (f *File) SetRequireSeparateIndirect(req []*Require) {
  1252  	// hasComments returns whether a line or block has comments
  1253  	// other than "indirect".
  1254  	hasComments := func(c Comments) bool {
  1255  		return len(c.Before) > 0 || len(c.After) > 0 || len(c.Suffix) > 1 ||
  1256  			(len(c.Suffix) == 1 &&
  1257  				strings.TrimSpace(strings.TrimPrefix(c.Suffix[0].Token, string(slashSlash))) != "indirect")
  1258  	}
  1259  
  1260  	// moveReq adds r to block. If r was in another block, moveReq deletes
  1261  	// it from that block and transfers its comments.
  1262  	moveReq := func(r *Require, block *LineBlock) {
  1263  		var line *Line
  1264  		if r.Syntax == nil {
  1265  			line = &Line{Token: []string{AutoQuote(r.Mod.Path), r.Mod.Version}}
  1266  			r.Syntax = line
  1267  			if r.Indirect {
  1268  				r.setIndirect(true)
  1269  			}
  1270  		} else {
  1271  			line = new(Line)
  1272  			*line = *r.Syntax
  1273  			if !line.InBlock && len(line.Token) > 0 && line.Token[0] == "require" {
  1274  				line.Token = line.Token[1:]
  1275  			}
  1276  			r.Syntax.Token = nil // Cleanup will delete the old line.
  1277  			r.Syntax = line
  1278  		}
  1279  		line.InBlock = true
  1280  		block.Line = append(block.Line, line)
  1281  	}
  1282  
  1283  	// Examine existing require lines and blocks.
  1284  	var (
  1285  		// We may insert new requirements into the last uncommented
  1286  		// direct-only and indirect-only blocks. We may also move requirements
  1287  		// to the opposite block if their indirect markings change.
  1288  		lastDirectIndex   = -1
  1289  		lastIndirectIndex = -1
  1290  
  1291  		// If there are no direct-only or indirect-only blocks, a new block may
  1292  		// be inserted after the last require line or block.
  1293  		lastRequireIndex = -1
  1294  
  1295  		// If there's only one require line or block, and it's uncommented,
  1296  		// we'll move its requirements to the direct-only or indirect-only blocks.
  1297  		requireLineOrBlockCount = 0
  1298  
  1299  		// Track the block each requirement belongs to (if any) so we can
  1300  		// move them later.
  1301  		lineToBlock = make(map[*Line]*LineBlock)
  1302  	)
  1303  	for i, stmt := range f.Syntax.Stmt {
  1304  		switch stmt := stmt.(type) {
  1305  		case *Line:
  1306  			if len(stmt.Token) == 0 || stmt.Token[0] != "require" {
  1307  				continue
  1308  			}
  1309  			lastRequireIndex = i
  1310  			requireLineOrBlockCount++
  1311  			if !hasComments(stmt.Comments) {
  1312  				if isIndirect(stmt) {
  1313  					lastIndirectIndex = i
  1314  				} else {
  1315  					lastDirectIndex = i
  1316  				}
  1317  			}
  1318  
  1319  		case *LineBlock:
  1320  			if len(stmt.Token) == 0 || stmt.Token[0] != "require" {
  1321  				continue
  1322  			}
  1323  			lastRequireIndex = i
  1324  			requireLineOrBlockCount++
  1325  			allDirect := len(stmt.Line) > 0 && !hasComments(stmt.Comments)
  1326  			allIndirect := len(stmt.Line) > 0 && !hasComments(stmt.Comments)
  1327  			for _, line := range stmt.Line {
  1328  				lineToBlock[line] = stmt
  1329  				if hasComments(line.Comments) {
  1330  					allDirect = false
  1331  					allIndirect = false
  1332  				} else if isIndirect(line) {
  1333  					allDirect = false
  1334  				} else {
  1335  					allIndirect = false
  1336  				}
  1337  			}
  1338  			if allDirect {
  1339  				lastDirectIndex = i
  1340  			}
  1341  			if allIndirect {
  1342  				lastIndirectIndex = i
  1343  			}
  1344  		}
  1345  	}
  1346  
  1347  	oneFlatUncommentedBlock := requireLineOrBlockCount == 1 &&
  1348  		!hasComments(*f.Syntax.Stmt[lastRequireIndex].Comment())
  1349  
  1350  	// Create direct and indirect blocks if needed. Convert lines into blocks
  1351  	// if needed. If we end up with an empty block or a one-line block,
  1352  	// Cleanup will delete it or convert it to a line later.
  1353  	insertBlock := func(i int) *LineBlock {
  1354  		block := &LineBlock{Token: []string{"require"}}
  1355  		f.Syntax.Stmt = append(f.Syntax.Stmt, nil)
  1356  		copy(f.Syntax.Stmt[i+1:], f.Syntax.Stmt[i:])
  1357  		f.Syntax.Stmt[i] = block
  1358  		return block
  1359  	}
  1360  
  1361  	ensureBlock := func(i int) *LineBlock {
  1362  		switch stmt := f.Syntax.Stmt[i].(type) {
  1363  		case *LineBlock:
  1364  			return stmt
  1365  		case *Line:
  1366  			block := &LineBlock{
  1367  				Token: []string{"require"},
  1368  				Line:  []*Line{stmt},
  1369  			}
  1370  			stmt.Token = stmt.Token[1:] // remove "require"
  1371  			stmt.InBlock = true
  1372  			f.Syntax.Stmt[i] = block
  1373  			return block
  1374  		default:
  1375  			panic(fmt.Sprintf("unexpected statement: %v", stmt))
  1376  		}
  1377  	}
  1378  
  1379  	var lastDirectBlock *LineBlock
  1380  	if lastDirectIndex < 0 {
  1381  		if lastIndirectIndex >= 0 {
  1382  			lastDirectIndex = lastIndirectIndex
  1383  			lastIndirectIndex++
  1384  		} else if lastRequireIndex >= 0 {
  1385  			lastDirectIndex = lastRequireIndex + 1
  1386  		} else {
  1387  			lastDirectIndex = len(f.Syntax.Stmt)
  1388  		}
  1389  		lastDirectBlock = insertBlock(lastDirectIndex)
  1390  	} else {
  1391  		lastDirectBlock = ensureBlock(lastDirectIndex)
  1392  	}
  1393  
  1394  	var lastIndirectBlock *LineBlock
  1395  	if lastIndirectIndex < 0 {
  1396  		lastIndirectIndex = lastDirectIndex + 1
  1397  		lastIndirectBlock = insertBlock(lastIndirectIndex)
  1398  	} else {
  1399  		lastIndirectBlock = ensureBlock(lastIndirectIndex)
  1400  	}
  1401  
  1402  	// Delete requirements we don't want anymore.
  1403  	// Update versions and indirect comments on requirements we want to keep.
  1404  	// If a requirement is in last{Direct,Indirect}Block with the wrong
  1405  	// indirect marking after this, or if the requirement is in an single
  1406  	// uncommented mixed block (oneFlatUncommentedBlock), move it to the
  1407  	// correct block.
  1408  	//
  1409  	// Some blocks may be empty after this. Cleanup will remove them.
  1410  	need := make(map[string]*Require)
  1411  	for _, r := range req {
  1412  		need[r.Mod.Path] = r
  1413  	}
  1414  	have := make(map[string]*Require)
  1415  	for _, r := range f.Require {
  1416  		path := r.Mod.Path
  1417  		if need[path] == nil || have[path] != nil {
  1418  			// Requirement not needed, or duplicate requirement. Delete.
  1419  			r.markRemoved()
  1420  			continue
  1421  		}
  1422  		have[r.Mod.Path] = r
  1423  		r.setVersion(need[path].Mod.Version)
  1424  		r.setIndirect(need[path].Indirect)
  1425  		if need[path].Indirect &&
  1426  			(oneFlatUncommentedBlock || lineToBlock[r.Syntax] == lastDirectBlock) {
  1427  			moveReq(r, lastIndirectBlock)
  1428  		} else if !need[path].Indirect &&
  1429  			(oneFlatUncommentedBlock || lineToBlock[r.Syntax] == lastIndirectBlock) {
  1430  			moveReq(r, lastDirectBlock)
  1431  		}
  1432  	}
  1433  
  1434  	// Add new requirements.
  1435  	for path, r := range need {
  1436  		if have[path] == nil {
  1437  			if r.Indirect {
  1438  				moveReq(r, lastIndirectBlock)
  1439  			} else {
  1440  				moveReq(r, lastDirectBlock)
  1441  			}
  1442  			f.Require = append(f.Require, r)
  1443  		}
  1444  	}
  1445  
  1446  	f.SortBlocks()
  1447  }
  1448  
  1449  func (f *File) DropGodebug(key string) error {
  1450  	for _, g := range f.Godebug {
  1451  		if g.Key == key {
  1452  			g.Syntax.markRemoved()
  1453  			*g = Godebug{}
  1454  		}
  1455  	}
  1456  	return nil
  1457  }
  1458  
  1459  func (f *File) DropRequire(path string) error {
  1460  	for _, r := range f.Require {
  1461  		if r.Mod.Path == path {
  1462  			r.Syntax.markRemoved()
  1463  			*r = Require{}
  1464  		}
  1465  	}
  1466  	return nil
  1467  }
  1468  
  1469  // AddExclude adds a exclude statement to the mod file. Errors if the provided
  1470  // version is not a canonical version string
  1471  func (f *File) AddExclude(path, vers string) error {
  1472  	if err := checkCanonicalVersion(path, vers); err != nil {
  1473  		return err
  1474  	}
  1475  
  1476  	var hint *Line
  1477  	for _, x := range f.Exclude {
  1478  		if x.Mod.Path == path && x.Mod.Version == vers {
  1479  			return nil
  1480  		}
  1481  		if x.Mod.Path == path {
  1482  			hint = x.Syntax
  1483  		}
  1484  	}
  1485  
  1486  	f.Exclude = append(f.Exclude, &Exclude{Mod: module.Version{Path: path, Version: vers}, Syntax: f.Syntax.addLine(hint, "exclude", AutoQuote(path), vers)})
  1487  	return nil
  1488  }
  1489  
  1490  func (f *File) DropExclude(path, vers string) error {
  1491  	for _, x := range f.Exclude {
  1492  		if x.Mod.Path == path && x.Mod.Version == vers {
  1493  			x.Syntax.markRemoved()
  1494  			*x = Exclude{}
  1495  		}
  1496  	}
  1497  	return nil
  1498  }
  1499  
  1500  func (f *File) AddReplace(oldPath, oldVers, newPath, newVers string) error {
  1501  	return addReplace(f.Syntax, &f.Replace, oldPath, oldVers, newPath, newVers)
  1502  }
  1503  
  1504  func addReplace(syntax *FileSyntax, replace *[]*Replace, oldPath, oldVers, newPath, newVers string) error {
  1505  	need := true
  1506  	old := module.Version{Path: oldPath, Version: oldVers}
  1507  	new := module.Version{Path: newPath, Version: newVers}
  1508  	tokens := []string{"replace", AutoQuote(oldPath)}
  1509  	if oldVers != "" {
  1510  		tokens = append(tokens, oldVers)
  1511  	}
  1512  	tokens = append(tokens, "=>", AutoQuote(newPath))
  1513  	if newVers != "" {
  1514  		tokens = append(tokens, newVers)
  1515  	}
  1516  
  1517  	var hint *Line
  1518  	for _, r := range *replace {
  1519  		if r.Old.Path == oldPath && (oldVers == "" || r.Old.Version == oldVers) {
  1520  			if need {
  1521  				// Found replacement for old; update to use new.
  1522  				r.New = new
  1523  				syntax.updateLine(r.Syntax, tokens...)
  1524  				need = false
  1525  				continue
  1526  			}
  1527  			// Already added; delete other replacements for same.
  1528  			r.Syntax.markRemoved()
  1529  			*r = Replace{}
  1530  		}
  1531  		if r.Old.Path == oldPath {
  1532  			hint = r.Syntax
  1533  		}
  1534  	}
  1535  	if need {
  1536  		*replace = append(*replace, &Replace{Old: old, New: new, Syntax: syntax.addLine(hint, tokens...)})
  1537  	}
  1538  	return nil
  1539  }
  1540  
  1541  func (f *File) DropReplace(oldPath, oldVers string) error {
  1542  	for _, r := range f.Replace {
  1543  		if r.Old.Path == oldPath && r.Old.Version == oldVers {
  1544  			r.Syntax.markRemoved()
  1545  			*r = Replace{}
  1546  		}
  1547  	}
  1548  	return nil
  1549  }
  1550  
  1551  // AddRetract adds a retract statement to the mod file. Errors if the provided
  1552  // version interval does not consist of canonical version strings
  1553  func (f *File) AddRetract(vi VersionInterval, rationale string) error {
  1554  	var path string
  1555  	if f.Module != nil {
  1556  		path = f.Module.Mod.Path
  1557  	}
  1558  	if err := checkCanonicalVersion(path, vi.High); err != nil {
  1559  		return err
  1560  	}
  1561  	if err := checkCanonicalVersion(path, vi.Low); err != nil {
  1562  		return err
  1563  	}
  1564  
  1565  	r := &Retract{
  1566  		VersionInterval: vi,
  1567  	}
  1568  	if vi.Low == vi.High {
  1569  		r.Syntax = f.Syntax.addLine(nil, "retract", AutoQuote(vi.Low))
  1570  	} else {
  1571  		r.Syntax = f.Syntax.addLine(nil, "retract", "[", AutoQuote(vi.Low), ",", AutoQuote(vi.High), "]")
  1572  	}
  1573  	if rationale != "" {
  1574  		for _, line := range strings.Split(rationale, "\n") {
  1575  			com := Comment{Token: "// " + line}
  1576  			r.Syntax.Comment().Before = append(r.Syntax.Comment().Before, com)
  1577  		}
  1578  	}
  1579  	return nil
  1580  }
  1581  
  1582  func (f *File) DropRetract(vi VersionInterval) error {
  1583  	for _, r := range f.Retract {
  1584  		if r.VersionInterval == vi {
  1585  			r.Syntax.markRemoved()
  1586  			*r = Retract{}
  1587  		}
  1588  	}
  1589  	return nil
  1590  }
  1591  
  1592  // AddTool adds a new tool directive with the given path.
  1593  // It does nothing if the tool line already exists.
  1594  func (f *File) AddTool(path string) error {
  1595  	for _, t := range f.Tool {
  1596  		if t.Path == path {
  1597  			return nil
  1598  		}
  1599  	}
  1600  
  1601  	f.Tool = append(f.Tool, &Tool{
  1602  		Path:   path,
  1603  		Syntax: f.Syntax.addLine(nil, "tool", path),
  1604  	})
  1605  
  1606  	f.SortBlocks()
  1607  	return nil
  1608  }
  1609  
  1610  // RemoveTool removes a tool directive with the given path.
  1611  // It does nothing if no such tool directive exists.
  1612  func (f *File) DropTool(path string) error {
  1613  	for _, t := range f.Tool {
  1614  		if t.Path == path {
  1615  			t.Syntax.markRemoved()
  1616  			*t = Tool{}
  1617  		}
  1618  	}
  1619  	return nil
  1620  }
  1621  
  1622  func (f *File) SortBlocks() {
  1623  	f.removeDups() // otherwise sorting is unsafe
  1624  
  1625  	// semanticSortForExcludeVersionV is the Go version (plus leading "v") at which
  1626  	// lines in exclude blocks start to use semantic sort instead of lexicographic sort.
  1627  	// See go.dev/issue/60028.
  1628  	const semanticSortForExcludeVersionV = "v1.21"
  1629  	useSemanticSortForExclude := f.Go != nil && semver.Compare("v"+f.Go.Version, semanticSortForExcludeVersionV) >= 0
  1630  
  1631  	for _, stmt := range f.Syntax.Stmt {
  1632  		block, ok := stmt.(*LineBlock)
  1633  		if !ok {
  1634  			continue
  1635  		}
  1636  		less := lineLess
  1637  		if block.Token[0] == "exclude" && useSemanticSortForExclude {
  1638  			less = lineExcludeLess
  1639  		} else if block.Token[0] == "retract" {
  1640  			less = lineRetractLess
  1641  		}
  1642  		sort.SliceStable(block.Line, func(i, j int) bool {
  1643  			return less(block.Line[i], block.Line[j])
  1644  		})
  1645  	}
  1646  }
  1647  
  1648  // removeDups removes duplicate exclude, replace and tool directives.
  1649  //
  1650  // Earlier exclude and tool directives take priority.
  1651  //
  1652  // Later replace directives take priority.
  1653  //
  1654  // require directives are not de-duplicated. That's left up to higher-level
  1655  // logic (MVS).
  1656  //
  1657  // retract directives are not de-duplicated since comments are
  1658  // meaningful, and versions may be retracted multiple times.
  1659  func (f *File) removeDups() {
  1660  	removeDups(f.Syntax, &f.Exclude, &f.Replace, &f.Tool)
  1661  }
  1662  
  1663  func removeDups(syntax *FileSyntax, exclude *[]*Exclude, replace *[]*Replace, tool *[]*Tool) {
  1664  	kill := make(map[*Line]bool)
  1665  
  1666  	// Remove duplicate excludes.
  1667  	if exclude != nil {
  1668  		haveExclude := make(map[module.Version]bool)
  1669  		for _, x := range *exclude {
  1670  			if haveExclude[x.Mod] {
  1671  				kill[x.Syntax] = true
  1672  				continue
  1673  			}
  1674  			haveExclude[x.Mod] = true
  1675  		}
  1676  		var excl []*Exclude
  1677  		for _, x := range *exclude {
  1678  			if !kill[x.Syntax] {
  1679  				excl = append(excl, x)
  1680  			}
  1681  		}
  1682  		*exclude = excl
  1683  	}
  1684  
  1685  	// Remove duplicate replacements.
  1686  	// Later replacements take priority over earlier ones.
  1687  	haveReplace := make(map[module.Version]bool)
  1688  	for i := len(*replace) - 1; i >= 0; i-- {
  1689  		x := (*replace)[i]
  1690  		if haveReplace[x.Old] {
  1691  			kill[x.Syntax] = true
  1692  			continue
  1693  		}
  1694  		haveReplace[x.Old] = true
  1695  	}
  1696  	var repl []*Replace
  1697  	for _, x := range *replace {
  1698  		if !kill[x.Syntax] {
  1699  			repl = append(repl, x)
  1700  		}
  1701  	}
  1702  	*replace = repl
  1703  
  1704  	if tool != nil {
  1705  		haveTool := make(map[string]bool)
  1706  		for _, t := range *tool {
  1707  			if haveTool[t.Path] {
  1708  				kill[t.Syntax] = true
  1709  				continue
  1710  			}
  1711  			haveTool[t.Path] = true
  1712  		}
  1713  		var newTool []*Tool
  1714  		for _, t := range *tool {
  1715  			if !kill[t.Syntax] {
  1716  				newTool = append(newTool, t)
  1717  			}
  1718  		}
  1719  		*tool = newTool
  1720  	}
  1721  
  1722  	// Duplicate require and retract directives are not removed.
  1723  
  1724  	// Drop killed statements from the syntax tree.
  1725  	var stmts []Expr
  1726  	for _, stmt := range syntax.Stmt {
  1727  		switch stmt := stmt.(type) {
  1728  		case *Line:
  1729  			if kill[stmt] {
  1730  				continue
  1731  			}
  1732  		case *LineBlock:
  1733  			var lines []*Line
  1734  			for _, line := range stmt.Line {
  1735  				if !kill[line] {
  1736  					lines = append(lines, line)
  1737  				}
  1738  			}
  1739  			stmt.Line = lines
  1740  			if len(lines) == 0 {
  1741  				continue
  1742  			}
  1743  		}
  1744  		stmts = append(stmts, stmt)
  1745  	}
  1746  	syntax.Stmt = stmts
  1747  }
  1748  
  1749  // lineLess returns whether li should be sorted before lj. It sorts
  1750  // lexicographically without assigning any special meaning to tokens.
  1751  func lineLess(li, lj *Line) bool {
  1752  	for k := 0; k < len(li.Token) && k < len(lj.Token); k++ {
  1753  		if li.Token[k] != lj.Token[k] {
  1754  			return li.Token[k] < lj.Token[k]
  1755  		}
  1756  	}
  1757  	return len(li.Token) < len(lj.Token)
  1758  }
  1759  
  1760  // lineExcludeLess reports whether li should be sorted before lj for lines in
  1761  // an "exclude" block.
  1762  func lineExcludeLess(li, lj *Line) bool {
  1763  	if len(li.Token) != 2 || len(lj.Token) != 2 {
  1764  		// Not a known exclude specification.
  1765  		// Fall back to sorting lexicographically.
  1766  		return lineLess(li, lj)
  1767  	}
  1768  	// An exclude specification has two tokens: ModulePath and Version.
  1769  	// Compare module path by string order and version by semver rules.
  1770  	if pi, pj := li.Token[0], lj.Token[0]; pi != pj {
  1771  		return pi < pj
  1772  	}
  1773  	return semver.Compare(li.Token[1], lj.Token[1]) < 0
  1774  }
  1775  
  1776  // lineRetractLess returns whether li should be sorted before lj for lines in
  1777  // a "retract" block. It treats each line as a version interval. Single versions
  1778  // are compared as if they were intervals with the same low and high version.
  1779  // Intervals are sorted in descending order, first by low version, then by
  1780  // high version, using semver.Compare.
  1781  func lineRetractLess(li, lj *Line) bool {
  1782  	interval := func(l *Line) VersionInterval {
  1783  		if len(l.Token) == 1 {
  1784  			return VersionInterval{Low: l.Token[0], High: l.Token[0]}
  1785  		} else if len(l.Token) == 5 && l.Token[0] == "[" && l.Token[2] == "," && l.Token[4] == "]" {
  1786  			return VersionInterval{Low: l.Token[1], High: l.Token[3]}
  1787  		} else {
  1788  			// Line in unknown format. Treat as an invalid version.
  1789  			return VersionInterval{}
  1790  		}
  1791  	}
  1792  	vii := interval(li)
  1793  	vij := interval(lj)
  1794  	if cmp := semver.Compare(vii.Low, vij.Low); cmp != 0 {
  1795  		return cmp > 0
  1796  	}
  1797  	return semver.Compare(vii.High, vij.High) > 0
  1798  }
  1799  
  1800  // checkCanonicalVersion returns a non-nil error if vers is not a canonical
  1801  // version string or does not match the major version of path.
  1802  //
  1803  // If path is non-empty, the error text suggests a format with a major version
  1804  // corresponding to the path.
  1805  func checkCanonicalVersion(path, vers string) error {
  1806  	_, pathMajor, pathMajorOk := module.SplitPathVersion(path)
  1807  
  1808  	if vers == "" || vers != module.CanonicalVersion(vers) {
  1809  		if pathMajor == "" {
  1810  			return &module.InvalidVersionError{
  1811  				Version: vers,
  1812  				Err:     fmt.Errorf("must be of the form v1.2.3"),
  1813  			}
  1814  		}
  1815  		return &module.InvalidVersionError{
  1816  			Version: vers,
  1817  			Err:     fmt.Errorf("must be of the form %s.2.3", module.PathMajorPrefix(pathMajor)),
  1818  		}
  1819  	}
  1820  
  1821  	if pathMajorOk {
  1822  		if err := module.CheckPathMajor(vers, pathMajor); err != nil {
  1823  			if pathMajor == "" {
  1824  				// In this context, the user probably wrote "v2.3.4" when they meant
  1825  				// "v2.3.4+incompatible". Suggest that instead of "v0 or v1".
  1826  				return &module.InvalidVersionError{
  1827  					Version: vers,
  1828  					Err:     fmt.Errorf("should be %s+incompatible (or module %s/%v)", vers, path, semver.Major(vers)),
  1829  				}
  1830  			}
  1831  			return err
  1832  		}
  1833  	}
  1834  
  1835  	return nil
  1836  }
  1837  

View as plain text