diff --git a/test/goroot/runner_test.go b/test/goroot/runner_test.go index 9cf94de330..1b7c6f22dd 100644 --- a/test/goroot/runner_test.go +++ b/test/goroot/runner_test.go @@ -12,6 +12,7 @@ import ( "go/constant" "go/format" "go/parser" + "go/scanner" "go/token" "io" "io/fs" @@ -1121,6 +1122,7 @@ func checkExpectedErrorsForFiles(output string, sources []diagnosticSource) erro lines = preferSpecificDiagnostics(lines) var wanted []wantedError sourceLines := make(map[string][]string, len(sources)) + physicalDiagnosticSources := make(map[string]bool, len(sources)) for _, source := range sources { expected, err := wantedErrors(source.full, source.short) if err != nil { @@ -1132,6 +1134,14 @@ func checkExpectedErrorsForFiles(output string, sources []diagnosticSource) erro return err } sourceLines[normalizeDiagnosticPath(source.short)] = strings.Split(string(data), "\n") + file := canonicalDiagnosticPath(source.full) + // Pairing is keyed by physical source lines. A line directive makes that + // relationship ambiguous, so leave every recovery diagnostic visible. + physical := !hasLineDirective(data) + if previous, ok := physicalDiagnosticSources[file]; ok { + physical = previous && physical + } + physicalDiagnosticSources[file] = physical } pathResolver := newDiagnosticPathResolver(sources) lexicalLocations := make(map[string]bool) @@ -1150,6 +1160,7 @@ func checkExpectedErrorsForFiles(output string, sources []diagnosticSource) erro continue } matched := false + parserRecoveryAuthorized := false for _, candidate := range candidates { diagnostic, ok := parseCompilerDiagnostic(candidate) sourceDiagnostic, sourceOK := parseSourceDiagnostic(candidate, pathResolver) @@ -1165,12 +1176,14 @@ func checkExpectedErrorsForFiles(output string, sources []diagnosticSource) erro if ok && isScopedLexicalDiagnostic(message) { lexicalLocations[diagnostic.locationKey()] = true } - if sourceOK { - if secondaries := parserRecoverySecondaries(message); len(secondaries) != 0 { + if !parserRecoveryAuthorized && sourceOK && physicalDiagnosticSources[sourceDiagnostic.file] { + groups := parserRecoverySecondaryGroups(message, expected.source) + for _, secondaries := range groups { parserRecoveryPairs = append(parserRecoveryPairs, parserRecoveryPair{ file: sourceDiagnostic.file, line: sourceDiagnostic.line, secondaries: secondaries, }) } + parserRecoveryAuthorized = len(groups) != 0 } if !ok { diagnostic = compilerDiagnostic{ @@ -1630,7 +1643,14 @@ func (resolver diagnosticPathResolver) resolve(file string) (string, bool) { return full, full != "" } if filepath.IsAbs(file) { - return canonicalDiagnosticPath(file), true + full := canonicalDiagnosticPath(file) + // An absolute path is not sufficient by itself: it must still identify + // one of the sources whose ERROR comments are being checked. + for _, source := range resolver { + if source == full { + return full, true + } + } } return "", false } @@ -1649,7 +1669,8 @@ func parseSourceDiagnostic(line string, resolver diagnosticPathResolver) (source } // parserRecoverySecondaries is deliberately limited to exact diagnostic pairs -// emitted by GOROOT cases enabled with this compatibility shim. +// whose secondary does not depend on the source shape. Source-dependent pairs +// belong in parserRecoverySourceSecondaries. func parserRecoverySecondaries(primary string) []string { switch primary { case "syntax error: cannot use a := 10 as value": @@ -1672,6 +1693,118 @@ func parserRecoverySecondaries(primary string) []string { return nil } +// parserRecoverySourceSecondaries handles diagnostic spellings shared by +// unrelated malformed programs. Exact source matching keeps those allowances +// scoped to the GOROOT cases that require them. +func parserRecoverySourceSecondaries(primary, source string) []string { + source = parserRecoverySourceCode(source) + switch primary { + // GOROOT/test/fixedbugs/bug050.go. go/parser omits the "syntax error:" + // prefix when the package clause is missing. + case "expected 'package', found 'func'": + if source == "func main() {" { + return []string{"expected ';', found '('"} + } + // GOROOT/test/syntax/vareq1.go + case "syntax error: unexpected { after top level declaration": + if source == `var x map[string]string{"a":"b"}` { + return []string{"expected ';', found '{'"} + } + // GOROOT/test/fixedbugs/bug228.go + case "syntax error: ... is missing type": + if source == "func g(x int, y float32) (...)" { + return []string{"expected type, found ')'"} + } + // GOROOT/test/syntax/chan1.go: channel send in an if condition. + case "syntax error: cannot use c <- v as value": + if source == "if c <- v {" { + return []string{"expected boolean expression, found simple statement (missing parentheses around composite literal?)"} + } + // GOROOT/test/syntax/chan1.go: channel send in a top-level declaration. + case "syntax error: unexpected <- after top level declaration": + if source == "var _ = c <- v" { + return []string{"expected ';', found '<-'"} + } + } + return nil +} + +// parserRecoverySecondaryGroups returns independent one-use allowances for an +// exact primary. Source-independent pairs are checked first, followed by +// source-dependent single groups. issue11610 deterministically emits two +// separate follow-ons, so each receives its own group here. +func parserRecoverySecondaryGroups(primary, source string) [][]string { + if secondaries := parserRecoverySecondaries(primary); len(secondaries) != 0 { + return [][]string{secondaries} + } + if secondaries := parserRecoverySourceSecondaries(primary, source); len(secondaries) != 0 { + return [][]string{secondaries} + } + source = parserRecoverySourceCode(source) + switch primary { + // GOROOT/test/fixedbugs/issue11610.go + case "invalid character U+003F '?'": + if source == "var?" { + return [][]string{ + {"expected 'IDENT', found 'ILLEGAL'"}, + {"illegal character U+003F '?'"}, + } + } + // GOROOT/test/syntax/ddd.go + case "syntax error: unexpected literal .3, expected name or (": + if source == "g(f..3)" { + return [][]string{ + {"expected selector or type assertion, found .3"}, + {"undefined: g"}, + {"f._ undefined (type func() has no field or method _)"}, + } + } + } + return nil +} + +func parserRecoverySourceCode(source string) string { + // Prefer the last recognized marker so marker-like text in the source + // expression cannot truncate the shape before the actual ERROR comment. + comment := -1 + for _, marker := range []string{"// ERROR", "// GC_ERROR"} { + if index := strings.LastIndex(source, marker); index > comment { + comment = index + } + } + if comment >= 0 { + source = source[:comment] + } + return strings.TrimSpace(source) +} + +func hasLineDirective(data []byte) bool { + file := token.NewFileSet().AddFile("", -1, len(data)) + var sourceScanner scanner.Scanner + sourceScanner.Init(file, data, func(token.Position, string) {}, scanner.ScanComments) + for { + position, kind, literal := sourceScanner.Scan() + if kind == token.EOF { + return false + } + if kind != token.COMMENT { + continue + } + if strings.HasPrefix(literal, "/*line ") && strings.Contains(literal[len("/*line "):], ":") { + return true + } + if !strings.HasPrefix(literal, "//line ") { + continue + } + offset := file.Offset(position) + lineStart := bytes.LastIndexByte(data[:offset], '\n') + 1 + if len(bytes.TrimSpace(data[lineStart:offset])) == 0 && + strings.Contains(literal[len("//line "):], ":") { + return true + } + } +} + func discardPairedParserDiagnostics(lines []string, resolver diagnosticPathResolver, pairs []parserRecoveryPair) []string { out := lines[:0] nextLine: diff --git a/test/goroot/runner_unit_test.go b/test/goroot/runner_unit_test.go index 08a2126c04..e3bd3b1b09 100644 --- a/test/goroot/runner_unit_test.go +++ b/test/goroot/runner_unit_test.go @@ -726,6 +726,427 @@ func TestCheckExpectedErrorsDiscardsExactParserPair(t *testing.T) { } } +func TestCheckExpectedErrorsDiscardsPairedDeclarationRecoveryDiagnostics(t *testing.T) { + tests := []struct { + name string + source string + wrong string + line int + primary string + secondary string + }{ + { + name: "missing package clause", + source: "func main() { // ERROR \"package\"\n}\n", + wrong: "func other() { // ERROR \"package\"\n}\n", + line: 1, + primary: "expected 'package', found 'func'", + secondary: "expected ';', found '('", + }, + { + name: "top-level composite literal", + source: `package p +var x map[string]string{"a":"b"} // ERROR "unexpected { at end of statement|unexpected { after top level declaration|expected ';' or newline after top level declaration" +`, + wrong: `package p +var y map[string]string{"a":"b"} // ERROR "unexpected { at end of statement|unexpected { after top level declaration|expected ';' or newline after top level declaration" +`, + line: 2, + primary: "syntax error: unexpected { after top level declaration", + secondary: "expected ';', found '{'", + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + file := filepath.Join(t.TempDir(), "case.go") + if err := os.WriteFile(file, []byte(tt.source), 0o644); err != nil { + t.Fatal(err) + } + output := fmt.Sprintf("%s:%d: %s\n%s:%d: %s\n", file, tt.line, tt.primary, file, tt.line, tt.secondary) + if err := checkExpectedErrors(output, file, "case.go"); err != nil { + t.Fatal(err) + } + if err := checkExpectedErrors(fmt.Sprintf("%s:%d: %s\n", file, tt.line, tt.secondary), file, "case.go"); err == nil { + t.Fatal("secondary diagnostic passed without its primary") + } + if got := parserRecoverySecondaries(tt.primary); got != nil { + t.Fatalf("source-dependent primary activated source-independent recovery: %v", got) + } + if got := parserRecoverySecondaries(tt.primary + "."); got != nil { + t.Fatalf("near-match primary activated parser recovery: %v", got) + } + + wrongFile := filepath.Join(t.TempDir(), "case.go") + if err := os.WriteFile(wrongFile, []byte(tt.wrong), 0o644); err != nil { + t.Fatal(err) + } + wrongOutput := fmt.Sprintf("%s:%d: %s\n%s:%d: %s\n", wrongFile, tt.line, tt.primary, wrongFile, tt.line, tt.secondary) + err := checkExpectedErrors(wrongOutput, wrongFile, "case.go") + if err == nil || !strings.Contains(err.Error(), tt.secondary) { + t.Fatalf("wrong source shape discarded secondary: %v", err) + } + }) + } +} + +func TestCheckExpectedErrorsDiscardsAdditionalParserRecoveryDiagnostics(t *testing.T) { + tests := []struct { + name string + source string + output func(file string) string + }{ + { + name: "variadic result Go 1.24", + source: `package p +func g(x int, y float32) (...) // ERROR "[.][.][.]" +`, + output: func(file string) string { + return file + ":2: syntax error: ... is missing type\n" + + file + ":2: expected type, found ')'\n" + }, + }, + { + name: "variadic result Go 1.25 and newer", + source: `package p +func g(x int, y float32) (...) // ERROR "[.][.][.]" +`, + output: func(file string) string { + return file + ":2: syntax error: ... is missing type\n" + + file + ":2: invalid use of ...\n" + + file + ":2: expected type, found ')'\n" + }, + }, + { + name: "channel send in if condition", + source: `package p +var c chan int +var v int +func f() { + if c <- v { // ERROR "cannot use c <- v as value|send statement used as value" + } +} +`, + output: func(file string) string { + return file + ":5: syntax error: cannot use c <- v as value\n" + + file + ":5: expected boolean expression, found simple statement (missing parentheses around composite literal?)\n" + }, + }, + { + name: "channel send at top level", + source: `package p +var c chan int +var v int +var _ = c <- v // ERROR "unexpected <-|send statement used as value" +`, + output: func(file string) string { + return file + ":4: syntax error: unexpected <- after top level declaration\n" + + file + ":4: expected ';', found '<-'\n" + }, + }, + { + name: "illegal declaration character", + source: `package p +var? // ERROR "invalid character U\+003F '\?'|invalid character 0x3f in input file" +`, + output: func(file string) string { + return file + ":2: invalid character U+003F '?'\n" + + file + ":2: expected 'IDENT', found 'ILLEGAL'\n" + + file + ":2: illegal character U+003F '?'\n" + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + file := filepath.Join(t.TempDir(), "case.go") + if err := os.WriteFile(file, []byte(tt.source), 0o644); err != nil { + t.Fatal(err) + } + if err := checkExpectedErrors(tt.output(file), file, "case.go"); err != nil { + t.Fatal(err) + } + }) + } +} + +func TestCheckExpectedErrorsDiscardsMalformedSelectorRecovery(t *testing.T) { + const ( + primary = "syntax error: unexpected literal .3, expected name or (" + source = `package p +func f() { + g(f..3) // ERROR "unexpected literal \.3, expected name or \(" +} +` + ) + secondaries := []string{ + "expected selector or type assertion, found .3", + "undefined: g", + "f._ undefined (type func() has no field or method _)", + } + output := func(file string, line int, messages ...string) string { + var lines []string + for _, message := range messages { + lines = append(lines, fmt.Sprintf("%s:%d: %s", file, line, message)) + } + return strings.Join(lines, "\n") + "\n" + } + writeSource := func(t *testing.T, contents string) string { + t.Helper() + file := filepath.Join(t.TempDir(), "case.go") + if err := os.WriteFile(file, []byte(contents), 0o644); err != nil { + t.Fatal(err) + } + return file + } + + file := writeSource(t, source) + messages := append([]string{primary}, secondaries...) + if err := checkExpectedErrors(output(file, 3, messages...), file, "case.go"); err != nil { + t.Fatal(err) + } + + t.Run("wrong source shape", func(t *testing.T) { + wrongFile := writeSource(t, strings.Replace(source, "g(f..3)", "h(f..3)", 1)) + err := checkExpectedErrors(output(wrongFile, 3, messages...), wrongFile, "case.go") + if err == nil || !strings.Contains(err.Error(), secondaries[0]) { + t.Fatalf("wrong source shape discarded recovery: %v", err) + } + }) + + t.Run("missing primary", func(t *testing.T) { + err := checkExpectedErrors(output(file, 3, secondaries...), file, "case.go") + if err == nil || !strings.Contains(err.Error(), secondaries[0]) { + t.Fatalf("recovery passed without its primary: %v", err) + } + }) + + t.Run("wrong line", func(t *testing.T) { + err := checkExpectedErrors(output(file, 3, primary)+output(file, 4, secondaries...), file, "case.go") + if err == nil || !strings.Contains(err.Error(), secondaries[0]) { + t.Fatalf("wrong-line recovery was discarded: %v", err) + } + }) +} + +func TestAdditionalParserRecoveryDiagnosticsFailOpen(t *testing.T) { + t.Run("one ERROR authorizes one recovery group", func(t *testing.T) { + file := filepath.Join(t.TempDir(), "case.go") + source := `package p +func f() { + if a := 10 { // ERROR "cannot use [ab] := 10 as value" + } +} +` + if err := os.WriteFile(file, []byte(source), 0o644); err != nil { + t.Fatal(err) + } + output := file + ":3: syntax error: cannot use a := 10 as value\n" + + file + ":3: syntax error: cannot use b := 10 as value\n" + + file + ":3: expected boolean expression, found assignment (missing parentheses around composite literal?)\n" + + file + ":3: expected boolean or range expression, found assignment (missing parentheses around composite literal?)\n" + err := checkExpectedErrors(output, file, "case.go") + if err == nil || !strings.Contains(err.Error(), "expected boolean or range expression") { + t.Fatalf("err=%v, want the second matching primary's recovery diagnostic to remain", err) + } + }) + + t.Run("missing primary", func(t *testing.T) { + file := filepath.Join(t.TempDir(), "case.go") + source := `package p +var? // ERROR "invalid character U\+003F '\?'" +` + if err := os.WriteFile(file, []byte(source), 0o644); err != nil { + t.Fatal(err) + } + output := file + ":2: expected 'IDENT', found 'ILLEGAL'\n" + + file + ":2: illegal character U+003F '?'\n" + err := checkExpectedErrors(output, file, "case.go") + if err == nil || !strings.Contains(err.Error(), `no match for "invalid character`) { + t.Fatalf("err=%v, want missing primary to fail", err) + } + }) + + t.Run("primary does not match ERROR", func(t *testing.T) { + file := filepath.Join(t.TempDir(), "case.go") + source := `package p +var? // ERROR "different diagnostic" +` + if err := os.WriteFile(file, []byte(source), 0o644); err != nil { + t.Fatal(err) + } + output := file + ":2: invalid character U+003F '?'\n" + + file + ":2: expected 'IDENT', found 'ILLEGAL'\n" + err := checkExpectedErrors(output, file, "case.go") + if err == nil || !strings.Contains(err.Error(), "expected 'IDENT', found 'ILLEGAL'") { + t.Fatalf("err=%v, want unmatched primary to leave recovery diagnostic visible", err) + } + }) + + t.Run("same line different illegal token", func(t *testing.T) { + file := filepath.Join(t.TempDir(), "case.go") + source := `package p +var _ = ?; var@ // ERROR "invalid character U\+003F '\?'" +` + if err := os.WriteFile(file, []byte(source), 0o644); err != nil { + t.Fatal(err) + } + output := file + ":2: invalid character U+003F '?'\n" + + file + ":2: expected 'IDENT', found 'ILLEGAL'\n" + err := checkExpectedErrors(output, file, "case.go") + if err == nil || !strings.Contains(err.Error(), "expected 'IDENT', found 'ILLEGAL'") { + t.Fatalf("err=%v, want the unrelated @ recovery diagnostic to remain", err) + } + }) + + t.Run("wrong line", func(t *testing.T) { + file := filepath.Join(t.TempDir(), "case.go") + source := `package p +var? // ERROR "invalid character U\+003F '\?'" + +` + if err := os.WriteFile(file, []byte(source), 0o644); err != nil { + t.Fatal(err) + } + output := file + ":2: invalid character U+003F '?'\n" + + file + ":3: expected 'IDENT', found 'ILLEGAL'\n" + err := checkExpectedErrors(output, file, "case.go") + if err == nil || !strings.Contains(err.Error(), ":3: expected 'IDENT', found 'ILLEGAL'") { + t.Fatalf("err=%v, want wrong-line recovery to remain", err) + } + }) + + t.Run("near match primary", func(t *testing.T) { + file := filepath.Join(t.TempDir(), "case.go") + source := `package p +var? // ERROR "invalid character U\+003F '\?'" +` + if err := os.WriteFile(file, []byte(source), 0o644); err != nil { + t.Fatal(err) + } + output := file + ":2: invalid character U+003F '?'.\n" + + file + ":2: expected 'IDENT', found 'ILLEGAL'\n" + err := checkExpectedErrors(output, file, "case.go") + if err == nil || !strings.Contains(err.Error(), "expected 'IDENT', found 'ILLEGAL'") { + t.Fatalf("err=%v, want recovery after near-match primary to remain", err) + } + }) + + t.Run("wrong channel source shape", func(t *testing.T) { + file := filepath.Join(t.TempDir(), "case.go") + source := `package p +var c chan int +var v int +func f() { + if (c <- v) { // ERROR "cannot use c <- v as value" + } +} +` + if err := os.WriteFile(file, []byte(source), 0o644); err != nil { + t.Fatal(err) + } + output := file + ":5: syntax error: cannot use c <- v as value\n" + + file + ":5: expected boolean expression, found simple statement (missing parentheses around composite literal?)\n" + err := checkExpectedErrors(output, file, "case.go") + if err == nil || !strings.Contains(err.Error(), "expected boolean expression") { + t.Fatalf("err=%v, want recovery for a different source shape to remain", err) + } + }) + + t.Run("wrong file", func(t *testing.T) { + dir := t.TempDir() + primaryFile := filepath.Join(dir, "primary.go") + otherFile := filepath.Join(dir, "other.go") + if err := os.WriteFile(primaryFile, []byte(`package p +var? // ERROR "invalid character U\+003F '\?'" +`), 0o644); err != nil { + t.Fatal(err) + } + if err := os.WriteFile(otherFile, []byte("package p\nvar x int\n"), 0o644); err != nil { + t.Fatal(err) + } + output := primaryFile + ":2: invalid character U+003F '?'\n" + + otherFile + ":2: expected 'IDENT', found 'ILLEGAL'\n" + err := checkExpectedErrorsForFiles(output, []diagnosticSource{ + {full: primaryFile, short: "primary.go"}, + {full: otherFile, short: "other.go"}, + }) + if err == nil || !strings.Contains(err.Error(), "other.go:2: expected 'IDENT', found 'ILLEGAL'") { + t.Fatalf("err=%v, want wrong-file recovery to remain", err) + } + }) + + t.Run("line directive", func(t *testing.T) { + file := filepath.Join(t.TempDir(), "case.go") + source := `package p +//line remapped.go:2 +var? // ERROR "invalid character U\+003F '\?'" +` + if err := os.WriteFile(file, []byte(source), 0o644); err != nil { + t.Fatal(err) + } + output := file + ":3: invalid character U+003F '?'\n" + + file + ":3: expected 'IDENT', found 'ILLEGAL'\n" + err := checkExpectedErrors(output, file, "case.go") + if err == nil || !strings.Contains(err.Error(), "expected 'IDENT', found 'ILLEGAL'") { + t.Fatalf("err=%v, want line-remapped recovery to remain", err) + } + }) +} + +func TestParserRecoverySourceCode(t *testing.T) { + tests := []struct { + name string + source string + want string + }{ + { + name: "URL before ERROR comment", + source: `var _ = "http://example.com" // ERROR "broken"`, + want: `var _ = "http://example.com"`, + }, + { + name: "URL without ERROR comment", + source: `var _ = "http://example.com"`, + want: `var _ = "http://example.com"`, + }, + { + name: "GC ERRORAUTO comment", + source: `var _ = "http://example.com" // GC_ERRORAUTO "broken"`, + want: `var _ = "http://example.com"`, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := parserRecoverySourceCode(tt.source); got != tt.want { + t.Fatalf("parserRecoverySourceCode(%q)=%q, want %q", tt.source, got, tt.want) + } + }) + } +} + +func TestHasLineDirective(t *testing.T) { + tests := []struct { + name string + data string + want bool + }{ + {name: "line comment", data: "\t//line remapped.go:1\n", want: true}, + {name: "block comment", data: " /*line remapped.go:1*/\n", want: true}, + {name: "block comment after source", data: "x /*line remapped.go:1*/\n", want: true}, + {name: "missing separator", data: "//linefoo.go:1\n", want: false}, + {name: "line comment without line number", data: "//line remapped\n", want: false}, + {name: "block comment without line number", data: "/*line remapped*/\n", want: false}, + {name: "after source", data: "x //line remapped.go:1\n", want: false}, + {name: "inside string", data: `var _ = "/*line remapped.go:1*/"` + "\n", want: false}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := hasLineDirective([]byte(tt.data)); got != tt.want { + t.Fatalf("hasLineDirective(%q)=%v, want %v", tt.data, got, tt.want) + } + }) + } +} + func TestCheckExpectedErrorsScopesImportAlias(t *testing.T) { tests := []struct { name, src string @@ -768,6 +1189,37 @@ func TestDiscardPairedParserDiagnosticsIsExactMultiset(t *testing.T) { if !reflect.DeepEqual(got, want) { t.Fatalf("discardPairedParserDiagnostics()=%v, want %v", got, want) } + + identifier := fileA + ":2: expected 'IDENT', found 'ILLEGAL'" + illegal := fileA + ":2: illegal character U+003F '?'" + pairs = []parserRecoveryPair{ + {file: canonicalDiagnosticPath(fileA), line: 2, secondaries: []string{"expected 'IDENT', found 'ILLEGAL'"}}, + {file: canonicalDiagnosticPath(fileA), line: 2, secondaries: []string{"illegal character U+003F '?'"}}, + } + got = discardPairedParserDiagnostics([]string{identifier, identifier, illegal, illegal}, resolver, pairs) + want = []string{identifier, illegal} + if !reflect.DeepEqual(got, want) { + t.Fatalf("independent groups=%v, want %v", got, want) + } +} + +func TestDiagnosticPathResolverRejectsUnknownAndAmbiguousSources(t *testing.T) { + dir := t.TempDir() + left := filepath.Join(dir, "left", "case.go") + right := filepath.Join(dir, "right", "case.go") + resolver := newDiagnosticPathResolver([]diagnosticSource{ + {full: left, short: "case.go"}, + {full: right, short: "case.go"}, + }) + if _, ok := resolver.resolve("case.go"); ok { + t.Fatal("ambiguous short path resolved") + } + if _, ok := resolver.resolve(filepath.Join(dir, "unknown", "case.go")); ok { + t.Fatal("unknown absolute path resolved") + } + if got, ok := resolver.resolve(left); !ok || got != canonicalDiagnosticPath(left) { + t.Fatalf("known absolute path resolved to %q, %v", got, ok) + } } func TestPreferSpecificDiagnostics(t *testing.T) { diff --git a/test/goroot/xfail.yaml b/test/goroot/xfail.yaml index 8183574996..61c4eb4077 100644 --- a/test/goroot/xfail.yaml +++ b/test/goroot/xfail.yaml @@ -2101,10 +2101,6 @@ xfails: directive: errorcheck case: typeparam/pragma.go reason: gc-specific pragma optimization diagnostics are not implemented by llgo - - version: go1.26 - directive: errorcheck - case: fixedbugs/bug050.go - reason: llgo parser emits an additional malformed-declaration diagnostic - version: go1.26 directive: errorcheck case: devirt.go @@ -2161,10 +2157,6 @@ xfails: directive: errorcheck case: typeparam/issue54497.go reason: gc-specific -m optimization diagnostics are not implemented by llgo - - version: go1.26 - directive: errorcheck - case: syntax/ddd.go - reason: llgo parser recovery emits additional malformed-selector diagnostics - version: go1.26 directive: errorcheck case: escape_hash_maphash.go @@ -2205,14 +2197,6 @@ xfails: directive: errorcheck case: fixedbugs/bug121.go reason: llgo parser recovery emits additional malformed-declaration diagnostics - - version: go1.26 - directive: errorcheck - case: fixedbugs/bug228.go - reason: llgo parser emits an additional malformed-parameter diagnostic - - version: go1.26 - directive: errorcheck - case: syntax/chan1.go - reason: llgo parser recovery emits additional malformed-channel diagnostics - version: go1.26 directive: errorcheck case: escape_field.go @@ -2273,10 +2257,6 @@ xfails: directive: errorcheck case: internal/runtime/sys/inlinegcpc.go reason: gc runtime inlining diagnostics are not implemented by llgo - - version: go1.26 - directive: errorcheck - case: fixedbugs/issue11610.go - reason: llgo parser reports additional illegal-character diagnostics - version: go1.26 directive: errorcheck case: fixedbugs/issue22164.go @@ -2309,10 +2289,6 @@ xfails: directive: errorcheck case: fixedbugs/issue34723.go reason: "not applicable: llgo's BDWGC and tinygogc runtimes do not use Go GC write barriers" - - version: go1.26 - directive: errorcheck - case: syntax/vareq1.go - reason: llgo parser recovery emits an additional diagnostic after the expected syntax error - version: go1.26 directive: errorcheck case: escape_bloop.go