github.com/jhump/golang-x-tools@v0.0.0-20220218190644-4958d6d39439/internal/lsp/regtest/expectation.go (about)

     1  // Copyright 2020 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 regtest
     6  
     7  import (
     8  	"fmt"
     9  	"regexp"
    10  	"strings"
    11  
    12  	"github.com/jhump/golang-x-tools/internal/lsp"
    13  	"github.com/jhump/golang-x-tools/internal/lsp/fake"
    14  	"github.com/jhump/golang-x-tools/internal/lsp/protocol"
    15  	"github.com/jhump/golang-x-tools/internal/testenv"
    16  )
    17  
    18  // An Expectation asserts that the state of the editor at a point in time
    19  // matches an expected condition. This is used for signaling in tests when
    20  // certain conditions in the editor are met.
    21  type Expectation interface {
    22  	// Check determines whether the state of the editor satisfies the
    23  	// expectation, returning the results that met the condition.
    24  	Check(State) Verdict
    25  	// Description is a human-readable description of the expectation.
    26  	Description() string
    27  }
    28  
    29  var (
    30  	// InitialWorkspaceLoad is an expectation that the workspace initial load has
    31  	// completed. It is verified via workdone reporting.
    32  	InitialWorkspaceLoad = CompletedWork(lsp.DiagnosticWorkTitle(lsp.FromInitialWorkspaceLoad), 1, false)
    33  )
    34  
    35  // A Verdict is the result of checking an expectation against the current
    36  // editor state.
    37  type Verdict int
    38  
    39  // Order matters for the following constants: verdicts are sorted in order of
    40  // decisiveness.
    41  const (
    42  	// Met indicates that an expectation is satisfied by the current state.
    43  	Met Verdict = iota
    44  	// Unmet indicates that an expectation is not currently met, but could be met
    45  	// in the future.
    46  	Unmet
    47  	// Unmeetable indicates that an expectation cannot be satisfied in the
    48  	// future.
    49  	Unmeetable
    50  )
    51  
    52  func (v Verdict) String() string {
    53  	switch v {
    54  	case Met:
    55  		return "Met"
    56  	case Unmet:
    57  		return "Unmet"
    58  	case Unmeetable:
    59  		return "Unmeetable"
    60  	}
    61  	return fmt.Sprintf("unrecognized verdict %d", v)
    62  }
    63  
    64  // SimpleExpectation holds an arbitrary check func, and implements the Expectation interface.
    65  type SimpleExpectation struct {
    66  	check       func(State) Verdict
    67  	description string
    68  }
    69  
    70  // Check invokes e.check.
    71  func (e SimpleExpectation) Check(s State) Verdict {
    72  	return e.check(s)
    73  }
    74  
    75  // Description returns e.descriptin.
    76  func (e SimpleExpectation) Description() string {
    77  	return e.description
    78  }
    79  
    80  // OnceMet returns an Expectation that, once the precondition is met, asserts
    81  // that mustMeet is met.
    82  func OnceMet(precondition Expectation, mustMeets ...Expectation) *SimpleExpectation {
    83  	check := func(s State) Verdict {
    84  		switch pre := precondition.Check(s); pre {
    85  		case Unmeetable:
    86  			return Unmeetable
    87  		case Met:
    88  			for _, mustMeet := range mustMeets {
    89  				verdict := mustMeet.Check(s)
    90  				if verdict != Met {
    91  					return Unmeetable
    92  				}
    93  			}
    94  			return Met
    95  		default:
    96  			return Unmet
    97  		}
    98  	}
    99  	var descriptions []string
   100  	for _, mustMeet := range mustMeets {
   101  		descriptions = append(descriptions, mustMeet.Description())
   102  	}
   103  	return &SimpleExpectation{
   104  		check:       check,
   105  		description: fmt.Sprintf("once %q is met, must have %q", precondition.Description(), strings.Join(descriptions, "\n")),
   106  	}
   107  }
   108  
   109  // ReadDiagnostics is an 'expectation' that is used to read diagnostics
   110  // atomically. It is intended to be used with 'OnceMet'.
   111  func ReadDiagnostics(fileName string, into *protocol.PublishDiagnosticsParams) *SimpleExpectation {
   112  	check := func(s State) Verdict {
   113  		diags, ok := s.diagnostics[fileName]
   114  		if !ok {
   115  			return Unmeetable
   116  		}
   117  		*into = *diags
   118  		return Met
   119  	}
   120  	return &SimpleExpectation{
   121  		check:       check,
   122  		description: fmt.Sprintf("read diagnostics for %q", fileName),
   123  	}
   124  }
   125  
   126  // NoOutstandingWork asserts that there is no work initiated using the LSP
   127  // $/progress API that has not completed.
   128  func NoOutstandingWork() SimpleExpectation {
   129  	check := func(s State) Verdict {
   130  		if len(s.outstandingWork) == 0 {
   131  			return Met
   132  		}
   133  		return Unmet
   134  	}
   135  	return SimpleExpectation{
   136  		check:       check,
   137  		description: "no outstanding work",
   138  	}
   139  }
   140  
   141  // NoShowMessage asserts that the editor has not received a ShowMessage.
   142  func NoShowMessage() SimpleExpectation {
   143  	check := func(s State) Verdict {
   144  		if len(s.showMessage) == 0 {
   145  			return Met
   146  		}
   147  		return Unmeetable
   148  	}
   149  	return SimpleExpectation{
   150  		check:       check,
   151  		description: "no ShowMessage received",
   152  	}
   153  }
   154  
   155  // ShownMessage asserts that the editor has received a ShownMessage with the
   156  // given title.
   157  func ShownMessage(title string) SimpleExpectation {
   158  	check := func(s State) Verdict {
   159  		for _, m := range s.showMessage {
   160  			if strings.Contains(m.Message, title) {
   161  				return Met
   162  			}
   163  		}
   164  		return Unmet
   165  	}
   166  	return SimpleExpectation{
   167  		check:       check,
   168  		description: "received ShowMessage",
   169  	}
   170  }
   171  
   172  // ShowMessageRequest asserts that the editor has received a ShowMessageRequest
   173  // with an action item that has the given title.
   174  func ShowMessageRequest(title string) SimpleExpectation {
   175  	check := func(s State) Verdict {
   176  		if len(s.showMessageRequest) == 0 {
   177  			return Unmet
   178  		}
   179  		// Only check the most recent one.
   180  		m := s.showMessageRequest[len(s.showMessageRequest)-1]
   181  		if len(m.Actions) == 0 || len(m.Actions) > 1 {
   182  			return Unmet
   183  		}
   184  		if m.Actions[0].Title == title {
   185  			return Met
   186  		}
   187  		return Unmet
   188  	}
   189  	return SimpleExpectation{
   190  		check:       check,
   191  		description: "received ShowMessageRequest",
   192  	}
   193  }
   194  
   195  // DoneWithOpen expects all didOpen notifications currently sent by the editor
   196  // to be completely processed.
   197  func (e *Env) DoneWithOpen() Expectation {
   198  	opens := e.Editor.Stats().DidOpen
   199  	return CompletedWork(lsp.DiagnosticWorkTitle(lsp.FromDidOpen), opens, true)
   200  }
   201  
   202  // StartedChange expects there to have been i work items started for
   203  // processing didChange notifications.
   204  func StartedChange(i uint64) Expectation {
   205  	return StartedWork(lsp.DiagnosticWorkTitle(lsp.FromDidChange), i)
   206  }
   207  
   208  // DoneWithChange expects all didChange notifications currently sent by the
   209  // editor to be completely processed.
   210  func (e *Env) DoneWithChange() Expectation {
   211  	changes := e.Editor.Stats().DidChange
   212  	return CompletedWork(lsp.DiagnosticWorkTitle(lsp.FromDidChange), changes, true)
   213  }
   214  
   215  // DoneWithSave expects all didSave notifications currently sent by the editor
   216  // to be completely processed.
   217  func (e *Env) DoneWithSave() Expectation {
   218  	saves := e.Editor.Stats().DidSave
   219  	return CompletedWork(lsp.DiagnosticWorkTitle(lsp.FromDidSave), saves, true)
   220  }
   221  
   222  // DoneWithChangeWatchedFiles expects all didChangeWatchedFiles notifications
   223  // currently sent by the editor to be completely processed.
   224  func (e *Env) DoneWithChangeWatchedFiles() Expectation {
   225  	changes := e.Editor.Stats().DidChangeWatchedFiles
   226  	return CompletedWork(lsp.DiagnosticWorkTitle(lsp.FromDidChangeWatchedFiles), changes, true)
   227  }
   228  
   229  // DoneWithClose expects all didClose notifications currently sent by the
   230  // editor to be completely processed.
   231  func (e *Env) DoneWithClose() Expectation {
   232  	changes := e.Editor.Stats().DidClose
   233  	return CompletedWork(lsp.DiagnosticWorkTitle(lsp.FromDidClose), changes, true)
   234  }
   235  
   236  // StartedWork expect a work item to have been started >= atLeast times.
   237  //
   238  // See CompletedWork.
   239  func StartedWork(title string, atLeast uint64) SimpleExpectation {
   240  	check := func(s State) Verdict {
   241  		if s.startedWork[title] >= atLeast {
   242  			return Met
   243  		}
   244  		return Unmet
   245  	}
   246  	return SimpleExpectation{
   247  		check:       check,
   248  		description: fmt.Sprintf("started work %q at least %d time(s)", title, atLeast),
   249  	}
   250  }
   251  
   252  // CompletedWork expects a work item to have been completed >= atLeast times.
   253  //
   254  // Since the Progress API doesn't include any hidden metadata, we must use the
   255  // progress notification title to identify the work we expect to be completed.
   256  func CompletedWork(title string, count uint64, atLeast bool) SimpleExpectation {
   257  	check := func(s State) Verdict {
   258  		if s.completedWork[title] == count || atLeast && s.completedWork[title] > count {
   259  			return Met
   260  		}
   261  		return Unmet
   262  	}
   263  	desc := fmt.Sprintf("completed work %q %v times", title, count)
   264  	if atLeast {
   265  		desc = fmt.Sprintf("completed work %q at least %d time(s)", title, count)
   266  	}
   267  	return SimpleExpectation{
   268  		check:       check,
   269  		description: desc,
   270  	}
   271  }
   272  
   273  // OutstandingWork expects a work item to be outstanding. The given title must
   274  // be an exact match, whereas the given msg must only be contained in the work
   275  // item's message.
   276  func OutstandingWork(title, msg string) SimpleExpectation {
   277  	check := func(s State) Verdict {
   278  		for _, work := range s.outstandingWork {
   279  			if work.title == title && strings.Contains(work.msg, msg) {
   280  				return Met
   281  			}
   282  		}
   283  		return Unmet
   284  	}
   285  	return SimpleExpectation{
   286  		check:       check,
   287  		description: fmt.Sprintf("outstanding work: %s", title),
   288  	}
   289  }
   290  
   291  // LogExpectation is an expectation on the log messages received by the editor
   292  // from gopls.
   293  type LogExpectation struct {
   294  	check       func([]*protocol.LogMessageParams) Verdict
   295  	description string
   296  }
   297  
   298  // Check implements the Expectation interface.
   299  func (e LogExpectation) Check(s State) Verdict {
   300  	return e.check(s.logs)
   301  }
   302  
   303  // Description implements the Expectation interface.
   304  func (e LogExpectation) Description() string {
   305  	return e.description
   306  }
   307  
   308  // NoErrorLogs asserts that the client has not received any log messages of
   309  // error severity.
   310  func NoErrorLogs() LogExpectation {
   311  	return NoLogMatching(protocol.Error, "")
   312  }
   313  
   314  // LogMatching asserts that the client has received a log message
   315  // of type typ matching the regexp re.
   316  func LogMatching(typ protocol.MessageType, re string, count int, atLeast bool) LogExpectation {
   317  	rec, err := regexp.Compile(re)
   318  	if err != nil {
   319  		panic(err)
   320  	}
   321  	check := func(msgs []*protocol.LogMessageParams) Verdict {
   322  		var found int
   323  		for _, msg := range msgs {
   324  			if msg.Type == typ && rec.Match([]byte(msg.Message)) {
   325  				found++
   326  			}
   327  		}
   328  		// Check for an exact or "at least" match.
   329  		if found == count || (found >= count && atLeast) {
   330  			return Met
   331  		}
   332  		return Unmet
   333  	}
   334  	desc := fmt.Sprintf("log message matching %q expected %v times", re, count)
   335  	if atLeast {
   336  		desc = fmt.Sprintf("log message matching %q expected at least %v times", re, count)
   337  	}
   338  	return LogExpectation{
   339  		check:       check,
   340  		description: desc,
   341  	}
   342  }
   343  
   344  // NoLogMatching asserts that the client has not received a log message
   345  // of type typ matching the regexp re. If re is an empty string, any log
   346  // message is considered a match.
   347  func NoLogMatching(typ protocol.MessageType, re string) LogExpectation {
   348  	var r *regexp.Regexp
   349  	if re != "" {
   350  		var err error
   351  		r, err = regexp.Compile(re)
   352  		if err != nil {
   353  			panic(err)
   354  		}
   355  	}
   356  	check := func(msgs []*protocol.LogMessageParams) Verdict {
   357  		for _, msg := range msgs {
   358  			if msg.Type != typ {
   359  				continue
   360  			}
   361  			if r == nil || r.Match([]byte(msg.Message)) {
   362  				return Unmeetable
   363  			}
   364  		}
   365  		return Met
   366  	}
   367  	return LogExpectation{
   368  		check:       check,
   369  		description: fmt.Sprintf("no log message matching %q", re),
   370  	}
   371  }
   372  
   373  // RegistrationExpectation is an expectation on the capability registrations
   374  // received by the editor from gopls.
   375  type RegistrationExpectation struct {
   376  	check       func([]*protocol.RegistrationParams) Verdict
   377  	description string
   378  }
   379  
   380  // Check implements the Expectation interface.
   381  func (e RegistrationExpectation) Check(s State) Verdict {
   382  	return e.check(s.registrations)
   383  }
   384  
   385  // Description implements the Expectation interface.
   386  func (e RegistrationExpectation) Description() string {
   387  	return e.description
   388  }
   389  
   390  // RegistrationMatching asserts that the client has received a capability
   391  // registration matching the given regexp.
   392  func RegistrationMatching(re string) RegistrationExpectation {
   393  	rec, err := regexp.Compile(re)
   394  	if err != nil {
   395  		panic(err)
   396  	}
   397  	check := func(params []*protocol.RegistrationParams) Verdict {
   398  		for _, p := range params {
   399  			for _, r := range p.Registrations {
   400  				if rec.Match([]byte(r.Method)) {
   401  					return Met
   402  				}
   403  			}
   404  		}
   405  		return Unmet
   406  	}
   407  	return RegistrationExpectation{
   408  		check:       check,
   409  		description: fmt.Sprintf("registration matching %q", re),
   410  	}
   411  }
   412  
   413  // UnregistrationExpectation is an expectation on the capability
   414  // unregistrations received by the editor from gopls.
   415  type UnregistrationExpectation struct {
   416  	check       func([]*protocol.UnregistrationParams) Verdict
   417  	description string
   418  }
   419  
   420  // Check implements the Expectation interface.
   421  func (e UnregistrationExpectation) Check(s State) Verdict {
   422  	return e.check(s.unregistrations)
   423  }
   424  
   425  // Description implements the Expectation interface.
   426  func (e UnregistrationExpectation) Description() string {
   427  	return e.description
   428  }
   429  
   430  // UnregistrationMatching asserts that the client has received an
   431  // unregistration whose ID matches the given regexp.
   432  func UnregistrationMatching(re string) UnregistrationExpectation {
   433  	rec, err := regexp.Compile(re)
   434  	if err != nil {
   435  		panic(err)
   436  	}
   437  	check := func(params []*protocol.UnregistrationParams) Verdict {
   438  		for _, p := range params {
   439  			for _, r := range p.Unregisterations {
   440  				if rec.Match([]byte(r.Method)) {
   441  					return Met
   442  				}
   443  			}
   444  		}
   445  		return Unmet
   446  	}
   447  	return UnregistrationExpectation{
   448  		check:       check,
   449  		description: fmt.Sprintf("unregistration matching %q", re),
   450  	}
   451  }
   452  
   453  // A DiagnosticExpectation is a condition that must be met by the current set
   454  // of diagnostics for a file.
   455  type DiagnosticExpectation struct {
   456  	// optionally, the position of the diagnostic and the regex used to calculate it.
   457  	pos *fake.Pos
   458  	re  string
   459  
   460  	// optionally, the message that the diagnostic should contain.
   461  	message string
   462  
   463  	// whether the expectation is that the diagnostic is present, or absent.
   464  	present bool
   465  
   466  	// path is the scratch workdir-relative path to the file being asserted on.
   467  	path string
   468  }
   469  
   470  // Check implements the Expectation interface.
   471  func (e DiagnosticExpectation) Check(s State) Verdict {
   472  	diags, ok := s.diagnostics[e.path]
   473  	if !ok {
   474  		if !e.present {
   475  			return Met
   476  		}
   477  		return Unmet
   478  	}
   479  
   480  	found := false
   481  	for _, d := range diags.Diagnostics {
   482  		if e.pos != nil {
   483  			if d.Range.Start.Line != uint32(e.pos.Line) || d.Range.Start.Character != uint32(e.pos.Column) {
   484  				continue
   485  			}
   486  		}
   487  		if e.message != "" {
   488  			if !strings.Contains(d.Message, e.message) {
   489  				continue
   490  			}
   491  		}
   492  		found = true
   493  		break
   494  	}
   495  
   496  	if found == e.present {
   497  		return Met
   498  	}
   499  	return Unmet
   500  }
   501  
   502  // Description implements the Expectation interface.
   503  func (e DiagnosticExpectation) Description() string {
   504  	desc := e.path + ":"
   505  	if !e.present {
   506  		desc += " no"
   507  	}
   508  	desc += " diagnostic"
   509  	if e.pos != nil {
   510  		desc += fmt.Sprintf(" at {line:%d, column:%d}", e.pos.Line, e.pos.Column)
   511  		if e.re != "" {
   512  			desc += fmt.Sprintf(" (location of %q)", e.re)
   513  		}
   514  	}
   515  	if e.message != "" {
   516  		desc += fmt.Sprintf(" with message %q", e.message)
   517  	}
   518  	return desc
   519  }
   520  
   521  // NoOutstandingDiagnostics asserts that the workspace has no outstanding
   522  // diagnostic messages.
   523  func NoOutstandingDiagnostics() Expectation {
   524  	check := func(s State) Verdict {
   525  		for _, diags := range s.diagnostics {
   526  			if len(diags.Diagnostics) > 0 {
   527  				return Unmet
   528  			}
   529  		}
   530  		return Met
   531  	}
   532  	return SimpleExpectation{
   533  		check:       check,
   534  		description: "no outstanding diagnostics",
   535  	}
   536  }
   537  
   538  // EmptyDiagnostics asserts that empty diagnostics are sent for the
   539  // workspace-relative path name.
   540  func EmptyDiagnostics(name string) Expectation {
   541  	check := func(s State) Verdict {
   542  		if diags := s.diagnostics[name]; diags != nil && len(diags.Diagnostics) == 0 {
   543  			return Met
   544  		}
   545  		return Unmet
   546  	}
   547  	return SimpleExpectation{
   548  		check:       check,
   549  		description: fmt.Sprintf("empty diagnostics for %q", name),
   550  	}
   551  }
   552  
   553  // EmptyOrNoDiagnostics asserts that either no diagnostics are sent for the
   554  // workspace-relative path name, or empty diagnostics are sent.
   555  // TODO(rFindley): this subtlety shouldn't be necessary. Gopls should always
   556  // send at least one diagnostic set for open files.
   557  func EmptyOrNoDiagnostics(name string) Expectation {
   558  	check := func(s State) Verdict {
   559  		if diags := s.diagnostics[name]; diags == nil || len(diags.Diagnostics) == 0 {
   560  			return Met
   561  		}
   562  		return Unmet
   563  	}
   564  	return SimpleExpectation{
   565  		check:       check,
   566  		description: fmt.Sprintf("empty or no diagnostics for %q", name),
   567  	}
   568  }
   569  
   570  // NoDiagnostics asserts that no diagnostics are sent for the
   571  // workspace-relative path name. It should be used primarily in conjunction
   572  // with a OnceMet, as it has to check that all outstanding diagnostics have
   573  // already been delivered.
   574  func NoDiagnostics(name string) Expectation {
   575  	check := func(s State) Verdict {
   576  		if _, ok := s.diagnostics[name]; !ok {
   577  			return Met
   578  		}
   579  		return Unmet
   580  	}
   581  	return SimpleExpectation{
   582  		check:       check,
   583  		description: "no diagnostics",
   584  	}
   585  }
   586  
   587  // AnyDiagnosticAtCurrentVersion asserts that there is a diagnostic report for
   588  // the current edited version of the buffer corresponding to the given
   589  // workdir-relative pathname.
   590  func (e *Env) AnyDiagnosticAtCurrentVersion(name string) Expectation {
   591  	version := e.Editor.BufferVersion(name)
   592  	check := func(s State) Verdict {
   593  		diags, ok := s.diagnostics[name]
   594  		if ok && diags.Version == int32(version) {
   595  			return Met
   596  		}
   597  		return Unmet
   598  	}
   599  	return SimpleExpectation{
   600  		check:       check,
   601  		description: fmt.Sprintf("any diagnostics at version %d", version),
   602  	}
   603  }
   604  
   605  // DiagnosticAtRegexp expects that there is a diagnostic entry at the start
   606  // position matching the regexp search string re in the buffer specified by
   607  // name. Note that this currently ignores the end position.
   608  func (e *Env) DiagnosticAtRegexp(name, re string) DiagnosticExpectation {
   609  	e.T.Helper()
   610  	pos := e.RegexpSearch(name, re)
   611  	return DiagnosticExpectation{path: name, pos: &pos, re: re, present: true}
   612  }
   613  
   614  // DiagnosticAtRegexpWithMessage is like DiagnosticAtRegexp, but it also
   615  // checks for the content of the diagnostic message,
   616  func (e *Env) DiagnosticAtRegexpWithMessage(name, re, msg string) DiagnosticExpectation {
   617  	e.T.Helper()
   618  	pos := e.RegexpSearch(name, re)
   619  	return DiagnosticExpectation{path: name, pos: &pos, re: re, present: true, message: msg}
   620  }
   621  
   622  // DiagnosticAt asserts that there is a diagnostic entry at the position
   623  // specified by line and col, for the workdir-relative path name.
   624  func DiagnosticAt(name string, line, col int) DiagnosticExpectation {
   625  	return DiagnosticExpectation{path: name, pos: &fake.Pos{Line: line, Column: col}, present: true}
   626  }
   627  
   628  // NoDiagnosticAtRegexp expects that there is no diagnostic entry at the start
   629  // position matching the regexp search string re in the buffer specified by
   630  // name. Note that this currently ignores the end position.
   631  // This should only be used in combination with OnceMet for a given condition,
   632  // otherwise it may always succeed.
   633  func (e *Env) NoDiagnosticAtRegexp(name, re string) DiagnosticExpectation {
   634  	e.T.Helper()
   635  	pos := e.RegexpSearch(name, re)
   636  	return DiagnosticExpectation{path: name, pos: &pos, re: re, present: false}
   637  }
   638  
   639  // NoDiagnosticAt asserts that there is no diagnostic entry at the position
   640  // specified by line and col, for the workdir-relative path name.
   641  // This should only be used in combination with OnceMet for a given condition,
   642  // otherwise it may always succeed.
   643  func NoDiagnosticAt(name string, line, col int) DiagnosticExpectation {
   644  	return DiagnosticExpectation{path: name, pos: &fake.Pos{Line: line, Column: col}, present: false}
   645  }
   646  
   647  // NoDiagnosticWithMessage asserts that there is no diagnostic entry with the
   648  // given message.
   649  //
   650  // This should only be used in combination with OnceMet for a given condition,
   651  // otherwise it may always succeed.
   652  func NoDiagnosticWithMessage(name, msg string) DiagnosticExpectation {
   653  	return DiagnosticExpectation{path: name, message: msg, present: false}
   654  }
   655  
   656  // GoSumDiagnostic asserts that a "go.sum is out of sync" diagnostic for the
   657  // given module (as formatted in a go.mod file, e.g. "example.com v1.0.0") is
   658  // present.
   659  func (e *Env) GoSumDiagnostic(name, module string) Expectation {
   660  	e.T.Helper()
   661  	// In 1.16, go.sum diagnostics should appear on the relevant module. Earlier
   662  	// errors have no information and appear on the module declaration.
   663  	if testenv.Go1Point() >= 16 {
   664  		return e.DiagnosticAtRegexpWithMessage(name, module, "go.sum is out of sync")
   665  	} else {
   666  		return e.DiagnosticAtRegexpWithMessage(name, `module`, "go.sum is out of sync")
   667  	}
   668  }