@@ -205,8 +205,6 @@ func Test(t *testing.T) {
205
205
}
206
206
}
207
207
208
- // Wait for the didOpen notifications to be processed, then collect
209
- // diagnostics.
210
208
for path , diags := range allDiags {
211
209
uri := run .env .Sandbox .Workdir .URI (path )
212
210
for _ , diag := range diags {
@@ -246,7 +244,13 @@ func Test(t *testing.T) {
246
244
if ! test .ignoreExtraDiags {
247
245
for loc , diags := range run .diags {
248
246
for _ , diag := range diags {
249
- t .Errorf ("%s: unexpected diagnostic: %q" , run .fmtLoc (loc ), diag .Message )
247
+ // Note that loc is collapsed (start==end).
248
+ // For formatting, show the exact span.
249
+ exactLoc := protocol.Location {
250
+ URI : loc .URI ,
251
+ Range : diag .Range ,
252
+ }
253
+ t .Errorf ("%s: unexpected diagnostic: %q" , run .fmtLoc (exactLoc ), diag .Message )
250
254
}
251
255
}
252
256
}
@@ -404,10 +408,11 @@ func valueMarkerFunc(fn any) func(marker) {
404
408
// called during the processing of action markers (e.g. @action("abc", 123))
405
409
// with marker arguments converted to function parameters. The provided
406
410
// function's first parameter must be of type 'marker', and it must not return
407
- // any values.
411
+ // any values. Any named arguments that may be used by the marker func must be
412
+ // listed in allowedNames.
408
413
//
409
414
// The provided fn should not mutate the test environment.
410
- func actionMarkerFunc (fn any ) func (marker ) {
415
+ func actionMarkerFunc (fn any , allowedNames ... string ) func (marker ) {
411
416
ftype := reflect .TypeOf (fn )
412
417
if ftype .NumIn () == 0 || ftype .In (0 ) != markerType {
413
418
panic (fmt .Sprintf ("action marker function %#v must accept marker as its first argument" , ftype ))
@@ -416,7 +421,21 @@ func actionMarkerFunc(fn any) func(marker) {
416
421
panic (fmt .Sprintf ("action marker function %#v cannot have results" , ftype ))
417
422
}
418
423
424
+ var allowed map [string ]bool
425
+ if len (allowedNames ) > 0 {
426
+ allowed = make (map [string ]bool )
427
+ for _ , name := range allowedNames {
428
+ allowed [name ] = true
429
+ }
430
+ }
431
+
419
432
return func (mark marker ) {
433
+ for name := range mark .note .NamedArgs {
434
+ if ! allowed [name ] {
435
+ mark .errorf ("unexpected named argument %q" , name )
436
+ }
437
+ }
438
+
420
439
args := append ([]any {mark }, mark .note .Args ... )
421
440
argValues , err := convertArgs (mark , ftype , args )
422
441
if err != nil {
@@ -470,6 +489,18 @@ func convertArgs(mark marker, ftype reflect.Type, args []any) ([]reflect.Value,
470
489
return argValues , nil
471
490
}
472
491
492
+ // namedArg returns the named argument for name, or the default value.
493
+ func namedArg [T any ](mark marker , name string , dflt T ) T {
494
+ if v , ok := mark .note .NamedArgs [name ]; ok {
495
+ if e , ok := v .(T ); ok {
496
+ return e
497
+ } else {
498
+ mark .errorf ("invalid value for %q: %v" , name , v )
499
+ }
500
+ }
501
+ return dflt
502
+ }
503
+
473
504
// is reports whether arg is a T.
474
505
func is [T any ](arg any ) bool {
475
506
_ , ok := arg .(T )
@@ -492,7 +523,7 @@ var actionMarkerFuncs = map[string]func(marker){
492
523
"codelenses" : actionMarkerFunc (codeLensesMarker ),
493
524
"complete" : actionMarkerFunc (completeMarker ),
494
525
"def" : actionMarkerFunc (defMarker ),
495
- "diag" : actionMarkerFunc (diagMarker ),
526
+ "diag" : actionMarkerFunc (diagMarker , "exact" ),
496
527
"documentlink" : actionMarkerFunc (documentLinkMarker ),
497
528
"foldingrange" : actionMarkerFunc (foldingRangeMarker ),
498
529
"format" : actionMarkerFunc (formatMarker ),
@@ -1659,7 +1690,8 @@ func locMarker(mark marker, loc protocol.Location) protocol.Location { return lo
1659
1690
// diagMarker implements the @diag marker. It eliminates diagnostics from
1660
1691
// the observed set in mark.test.
1661
1692
func diagMarker (mark marker , loc protocol.Location , re * regexp.Regexp ) {
1662
- if _ , ok := removeDiagnostic (mark , loc , re ); ! ok {
1693
+ exact := namedArg (mark , "exact" , false )
1694
+ if _ , ok := removeDiagnostic (mark , loc , exact , re ); ! ok {
1663
1695
mark .errorf ("no diagnostic at %v matches %q" , loc , re )
1664
1696
}
1665
1697
}
@@ -1670,12 +1702,13 @@ func diagMarker(mark marker, loc protocol.Location, re *regexp.Regexp) {
1670
1702
// from the unmatched set.
1671
1703
//
1672
1704
// If not found, it returns (protocol.Diagnostic{}, false).
1673
- func removeDiagnostic (mark marker , loc protocol.Location , re * regexp.Regexp ) (protocol.Diagnostic , bool ) {
1674
- loc .Range .End = loc .Range .Start // diagnostics ignore end position.
1675
- diags := mark .run .diags [loc ]
1705
+ func removeDiagnostic (mark marker , loc protocol.Location , matchEnd bool , re * regexp.Regexp ) (protocol.Diagnostic , bool ) {
1706
+ key := loc
1707
+ key .Range .End = key .Range .Start // diagnostics ignore end position.
1708
+ diags := mark .run .diags [key ]
1676
1709
for i , diag := range diags {
1677
- if re .MatchString (diag .Message ) {
1678
- mark .run .diags [loc ] = append (diags [:i ], diags [i + 1 :]... )
1710
+ if re .MatchString (diag .Message ) && ( ! matchEnd || diag . Range . End == loc . Range . End ) {
1711
+ mark .run .diags [key ] = append (diags [:i ], diags [i + 1 :]... )
1679
1712
return diag , true
1680
1713
}
1681
1714
}
@@ -2007,7 +2040,7 @@ func (mark marker) consumeExtraNotes(name string, f func(marker)) {
2007
2040
func quickfixMarker (mark marker , loc protocol.Location , re * regexp.Regexp , golden * Golden ) {
2008
2041
loc .Range .End = loc .Range .Start // diagnostics ignore end position.
2009
2042
// Find and remove the matching diagnostic.
2010
- diag , ok := removeDiagnostic (mark , loc , re )
2043
+ diag , ok := removeDiagnostic (mark , loc , false , re )
2011
2044
if ! ok {
2012
2045
mark .errorf ("no diagnostic at %v matches %q" , loc , re )
2013
2046
return
@@ -2027,7 +2060,7 @@ func quickfixMarker(mark marker, loc protocol.Location, re *regexp.Regexp, golde
2027
2060
func quickfixErrMarker (mark marker , loc protocol.Location , re * regexp.Regexp , wantErr stringMatcher ) {
2028
2061
loc .Range .End = loc .Range .Start // diagnostics ignore end position.
2029
2062
// Find and remove the matching diagnostic.
2030
- diag , ok := removeDiagnostic (mark , loc , re )
2063
+ diag , ok := removeDiagnostic (mark , loc , false , re )
2031
2064
if ! ok {
2032
2065
mark .errorf ("no diagnostic at %v matches %q" , loc , re )
2033
2066
return
0 commit comments