diff --git a/internal/packages/load.go b/internal/packages/load.go index 8cae7a59f7..53adc825b0 100644 --- a/internal/packages/load.go +++ b/internal/packages/load.go @@ -20,10 +20,15 @@ import ( "fmt" "go/ast" "go/scanner" + "go/token" "go/types" + "go/version" "log" "os" + pathpkg "path" + "path/filepath" "runtime" + "strconv" "strings" "sync" "unsafe" @@ -338,6 +343,7 @@ func loadPackageEx(dedup Deduper, ld *loader, lpkg *loaderPackage) { } lpkg.Syntax = files + normalizeEmbedDriverDiagnostics(lpkg.Errors, ld.Fset, files, packageGoVersion(ld, lpkg)) if ld.Config.Mode&NeedTypes == 0 { return } @@ -365,6 +371,12 @@ func loadPackageEx(dedup Deduper, ld *loader, lpkg *loaderPackage) { if path == "unsafe" { return types.Unsafe, nil } + // go/packages does not create import metadata for an absolute path. + // Report the validation error that cmd/compile would have returned + // instead of leaking the loader's "no metadata" implementation detail. + if pathpkg.IsAbs(path) { + return nil, fmt.Errorf("import path cannot be absolute path") + } // The imports map is keyed by import path. ipkg := lpkg.Imports[path] @@ -402,11 +414,7 @@ func loadPackageEx(dedup Deduper, ld *loader, lpkg *loaderPackage) { Error: appendError, Sizes: ld.sizes, // may be nil } - if goVersion, ok := loadGoVersions.Load(ld); ok && lpkg.initial { - tc.GoVersion = goVersion.(string) - } else if lpkg.Module != nil && lpkg.Module.GoVersion != "" { - tc.GoVersion = "go" + lpkg.Module.GoVersion - } + tc.GoVersion = packageGoVersion(ld, lpkg) if (ld.Mode & typecheckCgo) != 0 { if !typesinternalSetUsesCgo(tc) { appendError(packages.Error{ @@ -474,6 +482,167 @@ func loadPackageEx(dedup Deduper, ld *loader, lpkg *loaderPackage) { lpkg.IllTyped = illTyped } +func packageGoVersion(ld *loader, lpkg *loaderPackage) string { + if goVersion, ok := loadGoVersions.Load(ld); ok && lpkg.initial { + return goVersion.(string) + } + if lpkg.Module != nil && lpkg.Module.GoVersion != "" { + return "go" + lpkg.Module.GoVersion + } + return "" +} + +const embedPatternDriverDiagnostic = "pattern //: invalid pattern syntax" + +// normalizeEmbedDriverDiagnostics handles the two semantic checks that +// cmd/compile performs before parsing embed patterns. go list instead treats a +// trailing // token as a pattern and reports the driver error above. Keep that +// error for every other context; replacing it unconditionally would hide real +// unmatched-pattern diagnostics from compiler clients. +func normalizeEmbedDriverDiagnostics(errs []packages.Error, fset *token.FileSet, files []*ast.File, goVersion string) { + for i := range errs { + if errs[i].Msg != embedPatternDriverDiagnostic { + continue + } + for _, file := range files { + context := embedDirectiveContextAt(fset, file, errs[i].Pos) + switch { + case context == embedDirectiveLocalVar: + errs[i].Msg = "go:embed cannot apply to var inside func" + case context == embedDirectivePackageVar && version.IsValid(goVersion) && version.Compare(goVersion, "go1.16") < 0: + errs[i].Msg = fmt.Sprintf("go:embed requires go1.16 or later (-lang was set to %s; check go.mod)", goVersion) + } + if errs[i].Msg != embedPatternDriverDiagnostic { + break + } + } + } +} + +type embedDirectiveContext uint8 + +const ( + embedDirectiveUnknown embedDirectiveContext = iota + embedDirectivePackageVar + embedDirectiveLocalVar +) + +func embedDirectiveContextAt(fset *token.FileSet, file *ast.File, errorPos string) embedDirectiveContext { + if fset == nil || file == nil { + return embedDirectiveUnknown + } + for _, group := range file.Comments { + for _, comment := range group.List { + if !isEmbedDirectiveComment(comment) || !sameDiagnosticLine(errorPos, fset.Position(comment.Pos())) { + continue + } + if localVarHasDocComment(file, comment) { + return embedDirectiveLocalVar + } + for _, decl := range file.Decls { + gen, ok := decl.(*ast.GenDecl) + if ok && gen.Tok == token.VAR && genDeclHasDocComment(gen, comment) { + return embedDirectivePackageVar + } + } + } + } + return embedDirectiveUnknown +} + +func isEmbedDirectiveComment(comment *ast.Comment) bool { + if comment == nil || !strings.HasPrefix(comment.Text, "//") { + return false + } + text := strings.TrimSpace(strings.TrimPrefix(comment.Text, "//")) + if text == "go:embed" { + return true + } + if !strings.HasPrefix(text, "go:embed") || len(text) == len("go:embed") { + return false + } + next := text[len("go:embed")] + return next == ' ' || next == '\t' +} + +func sameDiagnosticLine(errorPos string, commentPos token.Position) bool { + if errorPos == "" || commentPos.Filename == "" || commentPos.Line == 0 { + return false + } + errorFile, errorLine, ok := diagnosticFileLine(errorPos) + if !ok || errorLine != commentPos.Line { + return false + } + errorFile = filepath.Clean(errorFile) + commentFile := filepath.Clean(commentPos.Filename) + if errorFile == commentFile { + return true + } + return !filepath.IsAbs(errorFile) && + (commentFile == errorFile || strings.HasSuffix(commentFile, string(filepath.Separator)+errorFile)) +} + +func diagnosticFileLine(pos string) (file string, line int, ok bool) { + lastColon := strings.LastIndexByte(pos, ':') + if lastColon < 0 { + return "", 0, false + } + last, err := strconv.Atoi(pos[lastColon+1:]) + if err != nil { + return "", 0, false + } + prefix := pos[:lastColon] + if lineColon := strings.LastIndexByte(prefix, ':'); lineColon >= 0 { + if parsedLine, err := strconv.Atoi(prefix[lineColon+1:]); err == nil { + return prefix[:lineColon], parsedLine, true + } + } + return prefix, last, true +} + +func localVarHasDocComment(file *ast.File, comment *ast.Comment) bool { + found := false + ast.Inspect(file, func(node ast.Node) bool { + if found { + return false + } + stmt, ok := node.(*ast.DeclStmt) + if !ok { + return true + } + gen, ok := stmt.Decl.(*ast.GenDecl) + if ok && gen.Tok == token.VAR && genDeclHasDocComment(gen, comment) { + found = true + } + return false + }) + return found +} + +func genDeclHasDocComment(gen *ast.GenDecl, comment *ast.Comment) bool { + if commentGroupContains(gen.Doc, comment) { + return true + } + for _, spec := range gen.Specs { + if value, ok := spec.(*ast.ValueSpec); ok && commentGroupContains(value.Doc, comment) { + return true + } + } + return false +} + +func commentGroupContains(group *ast.CommentGroup, comment *ast.Comment) bool { + if group == nil { + return false + } + for _, candidate := range group.List { + if candidate == comment { + return true + } + } + return false +} + func loadRecursiveEx(dedup Deduper, ld *loader, lpkg *loaderPackage) { lpkg.loadOnce.Do(func() { // Load the direct dependencies, in parallel. diff --git a/internal/packages/load_test.go b/internal/packages/load_test.go index 956c3bb413..a65dfb7955 100644 --- a/internal/packages/load_test.go +++ b/internal/packages/load_test.go @@ -3,8 +3,12 @@ package packages import ( + "go/ast" + "go/parser" + "go/token" "os" "path/filepath" + "strconv" "strings" "testing" ) @@ -47,6 +51,250 @@ func identity[T any](value T) T { return value } } } +func TestNormalizeEmbedDriverDiagnostics(t *testing.T) { + tests := []struct { + name string + source string + errorLine int + goVersion string + want string + }{ + { + name: "local var", + source: `package p +func f() { + //go:embed x.txt // ERROR + var x string +}`, + errorLine: 3, + goVersion: "go1.24", + want: "go:embed cannot apply to var inside func", + }, + { + name: "old language version", + source: `package p +//go:embed x.txt // ERROR +var x string`, + errorLine: 2, + goVersion: "go1.15", + want: "go:embed requires go1.16 or later (-lang was set to go1.15; check go.mod)", + }, + { + name: "current language version remains driver error", + source: `package p +//go:embed x.txt // ERROR +var x string`, + errorLine: 2, + goVersion: "go1.24", + want: embedPatternDriverDiagnostic, + }, + { + name: "wrong source line remains driver error", + source: `package p +func f() { + //go:embed x.txt // ERROR + var x string +}`, + errorLine: 4, + goVersion: "go1.24", + want: embedPatternDriverDiagnostic, + }, + { + name: "non-var directive remains driver error", + source: `package p +//go:embed x.txt // ERROR +const x = ""`, + errorLine: 2, + goVersion: "go1.15", + want: embedPatternDriverDiagnostic, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + fset := token.NewFileSet() + const filename = "/tmp/embedcase.go" + file, err := parser.ParseFile(fset, filename, tt.source, parser.ParseComments) + if err != nil { + t.Fatal(err) + } + errs := []Error{ + {Pos: filename + ":" + strconv.Itoa(tt.errorLine) + ":20", Msg: embedPatternDriverDiagnostic}, + {Pos: filename + ":1:1", Msg: "unrelated driver diagnostic"}, + } + normalizeEmbedDriverDiagnostics(errs, fset, []*ast.File{file}, tt.goVersion) + if errs[0].Msg != tt.want { + t.Fatalf("normalized embed error = %q, want %q", errs[0].Msg, tt.want) + } + if errs[1].Msg != "unrelated driver diagnostic" { + t.Fatalf("unrelated error changed to %q", errs[1].Msg) + } + }) + } +} + +func TestEmbedDiagnosticHelperBoundaries(t *testing.T) { + t.Run("directive comments", func(t *testing.T) { + tests := []struct { + name string + comment *ast.Comment + want bool + }{ + {name: "nil"}, + {name: "block comment", comment: &ast.Comment{Text: "/*go:embed x*/"}}, + {name: "exact directive", comment: &ast.Comment{Text: "//go:embed"}, want: true}, + {name: "different comment", comment: &ast.Comment{Text: "// not a directive"}}, + {name: "lookalike directive", comment: &ast.Comment{Text: "//go:embedx"}}, + {name: "directive argument", comment: &ast.Comment{Text: "//go:embed x"}, want: true}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := isEmbedDirectiveComment(tt.comment); got != tt.want { + t.Fatalf("isEmbedDirectiveComment() = %v, want %v", got, tt.want) + } + }) + } + }) + + t.Run("nil syntax context", func(t *testing.T) { + if got := embedDirectiveContextAt(nil, &ast.File{}, "case.go:1:1"); got != embedDirectiveUnknown { + t.Fatalf("nil fileset context = %v, want unknown", got) + } + if got := embedDirectiveContextAt(token.NewFileSet(), nil, "case.go:1:1"); got != embedDirectiveUnknown { + t.Fatalf("nil file context = %v, want unknown", got) + } + }) + + t.Run("diagnostic positions", func(t *testing.T) { + commentPos := token.Position{Filename: filepath.Join("tmp", "case.go"), Line: 12} + tests := []struct { + name string + errorPos string + comment token.Position + want bool + }{ + {name: "empty error position", comment: commentPos}, + {name: "empty comment position", errorPos: "case.go:12:3"}, + {name: "missing separator", errorPos: "case.go", comment: commentPos}, + {name: "invalid line", errorPos: "case.go:line", comment: commentPos}, + {name: "wrong line", errorPos: "case.go:11:3", comment: commentPos}, + {name: "relative suffix", errorPos: "case.go:12:3", comment: commentPos, want: true}, + {name: "different file", errorPos: "other.go:12:3", comment: commentPos}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := sameDiagnosticLine(tt.errorPos, tt.comment); got != tt.want { + t.Fatalf("sameDiagnosticLine(%q, %+v) = %v, want %v", tt.errorPos, tt.comment, got, tt.want) + } + }) + } + }) + + t.Run("position parsing", func(t *testing.T) { + tests := []struct { + position string + wantFile string + wantLine int + wantOK bool + }{ + {position: "case.go"}, + {position: "case.go:line"}, + {position: "case.go:12", wantFile: "case.go", wantLine: 12, wantOK: true}, + {position: "case.go:12:3", wantFile: "case.go", wantLine: 12, wantOK: true}, + } + for _, tt := range tests { + file, line, ok := diagnosticFileLine(tt.position) + if file != tt.wantFile || line != tt.wantLine || ok != tt.wantOK { + t.Fatalf("diagnosticFileLine(%q) = (%q, %d, %v), want (%q, %d, %v)", tt.position, file, line, ok, tt.wantFile, tt.wantLine, tt.wantOK) + } + } + }) + + t.Run("declaration comments", func(t *testing.T) { + directive := &ast.Comment{Text: "//go:embed x"} + other := &ast.Comment{Text: "// other"} + matching := &ast.GenDecl{Specs: []ast.Spec{ + &ast.ValueSpec{}, + &ast.ValueSpec{Doc: &ast.CommentGroup{List: []*ast.Comment{nil, other, directive}}}, + }} + if !genDeclHasDocComment(matching, directive) { + t.Fatal("value-spec directive was not found") + } + missing := &ast.GenDecl{Specs: []ast.Spec{ + &ast.ValueSpec{Doc: &ast.CommentGroup{List: []*ast.Comment{other}}}, + }} + if genDeclHasDocComment(missing, directive) { + t.Fatal("unrelated value-spec comment matched directive") + } + }) +} + +func TestLoadExNormalizesFrontendDiagnostics(t *testing.T) { + t.Run("embed local var", func(t *testing.T) { + dir := t.TempDir() + writeLoadTestFile(t, filepath.Join(dir, "go.mod"), "module example.com/embedlocal\ngo 1.24\n") + writeLoadTestFile(t, filepath.Join(dir, "x.txt"), "x") + writeLoadTestFile(t, filepath.Join(dir, "load.go"), `package embedlocal +import _ "embed" +func f() { + //go:embed x.txt // ERROR + var x string + _ = x +}`) + pkg := loadOnePackage(t, dir, "go1.24") + assertPackageError(t, pkg, "go:embed cannot apply to var inside func") + assertPackageErrorAbsent(t, pkg, embedPatternDriverDiagnostic) + }) + + t.Run("embed language version", func(t *testing.T) { + dir := t.TempDir() + writeLoadTestFile(t, filepath.Join(dir, "go.mod"), "module example.com/embedversion\ngo 1.24\n") + writeLoadTestFile(t, filepath.Join(dir, "x.txt"), "x") + writeLoadTestFile(t, filepath.Join(dir, "load.go"), `package embedversion +import _ "embed" +//go:embed x.txt // ERROR +var x string`) + pkg := loadOnePackage(t, dir, "go1.15") + assertPackageError(t, pkg, "go:embed requires go1.16 or later") + assertPackageErrorAbsent(t, pkg, embedPatternDriverDiagnostic) + }) + + t.Run("absolute import", func(t *testing.T) { + dir := t.TempDir() + writeLoadTestFile(t, filepath.Join(dir, "go.mod"), "module example.com/absoluteimport\ngo 1.24\n") + writeLoadTestFile(t, filepath.Join(dir, "load.go"), "package absoluteimport\nimport _ \"/foo\"\n") + pkg := loadOnePackage(t, dir, "go1.24") + assertPackageError(t, pkg, "import path cannot be absolute path") + assertPackageErrorAbsent(t, pkg, "no metadata for /foo") + }) + +} + +func loadOnePackage(t *testing.T, dir, goVersion string) *Package { + t.Helper() + pkgs, err := LoadExWithGoVersion(nil, nil, loadTestConfig(dir), goVersion, ".") + if err != nil { + t.Fatal(err) + } + if len(pkgs) != 1 { + t.Fatalf("load returned %d packages, want 1", len(pkgs)) + } + return pkgs[0] +} + +func assertPackageError(t *testing.T, pkg *Package, text string) { + t.Helper() + if !packageErrorsContain(pkg, text) { + t.Fatalf("package errors do not contain %q: %+v", text, pkg.Errors) + } +} + +func assertPackageErrorAbsent(t *testing.T, pkg *Package, text string) { + t.Helper() + if packageErrorsContain(pkg, text) { + t.Fatalf("package errors unexpectedly contain %q: %+v", text, pkg.Errors) + } +} + func loadTestConfig(dir string) *Config { return &Config{ Dir: dir, diff --git a/test/go/tool_compile_compat_test.go b/test/go/tool_compile_compat_test.go index 4d1ff3903f..3d4d7c8eb9 100644 --- a/test/go/tool_compile_compat_test.go +++ b/test/go/tool_compile_compat_test.go @@ -89,6 +89,47 @@ var _ = missing }) } +func TestToolCompileFrontendDiagnosticNormalization(t *testing.T) { + t.Run("absolute import", func(t *testing.T) { + dir := t.TempDir() + writeToolCompileSource(t, dir, "invalid.go", "package compat\nimport _ \"/foo\"\n") + writeToolCompileSource(t, dir, "importcfg", "") + compareToolCompileResult(t, dir, []string{ + "-C", "-e", "-importcfg=importcfg", "-o=invalid.o", "invalid.go", + }, false, "import path cannot be absolute path") + }) + + t.Run("embed local var", func(t *testing.T) { + dir := t.TempDir() + writeToolCompileStdlibImportCfg(t, dir) + writeToolCompileSource(t, dir, "x.txt", "x") + writeToolCompileSource(t, dir, "invalid.go", `package compat +import _ "embed" +func f() { + //go:embed x.txt // ERROR + var x string + _ = x +}`) + compareToolCompileResult(t, dir, []string{ + "-C", "-e", "-importcfg=importcfg", "-o=invalid.o", "invalid.go", + }, false, "go:embed cannot apply to var inside func") + }) + + t.Run("embed language version", func(t *testing.T) { + dir := t.TempDir() + writeToolCompileStdlibImportCfg(t, dir) + writeToolCompileSource(t, dir, "x.txt", "x") + writeToolCompileSource(t, dir, "invalid.go", `package compat +import _ "embed" +//go:embed x.txt // ERROR +var x string`) + compareToolCompileResult(t, dir, []string{ + "-C", "-e", "-lang=go1.15", "-importcfg=importcfg", "-o=invalid.o", "invalid.go", + }, false, "go:embed requires go1.16 or later") + }) + +} + func compareToolCompileResult(t *testing.T, dir string, args []string, wantSuccess bool, wantText string) (goOutput, llgoOutput string) { t.Helper() @@ -123,3 +164,14 @@ func writeToolCompileSource(t *testing.T, dir, name, content string) { t.Fatal(err) } } + +func writeToolCompileStdlibImportCfg(t *testing.T, dir string) { + t.Helper() + cmd := exec.Command("go", "list", "-export", "-f", "{{if .Export}}packagefile {{.ImportPath}}={{.Export}}{{end}}", "std") + cmd.Env = append(os.Environ(), "GOENV=off", "GOFLAGS=") + output, err := cmd.CombinedOutput() + if err != nil { + t.Fatalf("list stdlib export files: %v\n%s", err, output) + } + writeToolCompileSource(t, dir, "importcfg", string(output)) +} diff --git a/test/goroot/runner_test.go b/test/goroot/runner_test.go index 0401b0f121..d7cb091f1b 100644 --- a/test/goroot/runner_test.go +++ b/test/goroot/runner_test.go @@ -1230,6 +1230,7 @@ func normalizeCompilerDiagnostics(lines []string) []string { } func normalizeCompilerDiagnosticMessage(message string) string { + message = normalizeGoTypesDiagnosticMessage(message) switch message { case "continue not in for statement": return "continue is not in a loop" @@ -1260,6 +1261,57 @@ func normalizeCompilerDiagnosticMessage(message string) string { return message } +// normalizeGoTypesDiagnosticMessage maps wording differences in llgo's +// go/types frontend to the equivalent cmd/compile diagnostics expected by +// GOROOT errorcheck cases. It intentionally affects only the test harness. +func normalizeGoTypesDiagnosticMessage(message string) string { + const importPrefix = `non-canonical import path "` + if rest, ok := strings.CutPrefix(message, importPrefix); ok { + if bad, good, ok := strings.Cut(rest, `": should be "`); ok && strings.HasSuffix(good, `"`) { + if _, badErr := strconv.Unquote(`"` + bad + `"`); badErr == nil { + if _, goodErr := strconv.Unquote(`"` + good); goodErr == nil { + return importPrefix + bad + `" (should be "` + good + `)` + } + } + } + } + + for _, prefix := range []string{"cannot refer to unexported field ", "unknown field "} { + rest, ok := strings.CutPrefix(message, prefix) + if !ok { + continue + } + name, suffix, ok := strings.Cut(rest, " in struct literal of type ") + if !ok || !token.IsIdentifier(name) { + continue + } + message = prefix + "'" + name + "' in struct literal of type " + suffix + if before, suggestion, ok := strings.Cut(message, ", but does have "); ok { + return before + " (but does have " + suggestion + ")" + } + return message + } + + selector, detail, ok := strings.Cut(message, " undefined (") + if !ok || selector == "" || !strings.HasSuffix(detail, ")") { + return message + } + detail = strings.TrimSuffix(detail, ")") + for _, kind := range []string{"field", "method"} { + prefix := "cannot refer to unexported " + kind + " " + if name, ok := strings.CutPrefix(detail, prefix); ok && token.IsIdentifier(name) { + return selector + " undefined (cannot refer to unexported field or method " + name + ")" + } + } + for _, kind := range []string{"field", "method"} { + marker := ", but does have " + kind + " " + if before, name, ok := strings.Cut(detail, marker); ok && token.IsIdentifier(name) { + return selector + " undefined (" + before + ", but does have " + name + ")" + } + } + return message +} + func normalizeDiagnosticPath(value string) string { return filepath.ToSlash(filepath.Clean(value)) } diff --git a/test/goroot/runner_unit_test.go b/test/goroot/runner_unit_test.go index e59c450df5..33e079385b 100644 --- a/test/goroot/runner_unit_test.go +++ b/test/goroot/runner_unit_test.go @@ -744,6 +744,31 @@ func TestNormalizeCompilerDiagnosticMessage(t *testing.T) { {name: "break", in: "break not in for, switch, or select statement", want: "break is not in a loop, switch, or select"}, {name: "range count", in: "expected at most 2 expressions", want: "range clause permits at most two iteration variables"}, {name: "type switch", in: "invalid syntax tree: incorrect form of type switch guard", want: "invalid variable name in type switch guard"}, + { + name: "non-canonical import", + in: `non-canonical import path "unicode//utf8": should be "unicode/utf8"`, + want: `non-canonical import path "unicode//utf8" (should be "unicode/utf8")`, + }, + { + name: "unexported struct field", + in: "cannot refer to unexported field doneChan in struct literal of type f1.Foo", + want: "cannot refer to unexported field 'doneChan' in struct literal of type f1.Foo", + }, + { + name: "unknown struct field", + in: "unknown field DoneChan in struct literal of type f1.Foo, but does have unexported doneChan", + want: "unknown field 'DoneChan' in struct literal of type f1.Foo (but does have unexported doneChan)", + }, + { + name: "unexported selector", + in: "f.doneChan undefined (cannot refer to unexported field doneChan)", + want: "f.doneChan undefined (cannot refer to unexported field or method doneChan)", + }, + { + name: "nearby selector", + in: "f.name undefined (type f1.Foo has no field or method name, but does have field Name)", + want: "f.name undefined (type f1.Foo has no field or method name, but does have Name)", + }, {name: "initialization", in: "initialization cycle for a", want: "initialization cycle for a"}, {name: "goto position", in: "goto L jumps over variable declaration at line 43", want: "goto jumps over declaration"}, {name: "unused label", in: "label L declared and not used", want: "label L defined and not used"}, @@ -757,6 +782,8 @@ func TestNormalizeCompilerDiagnosticMessage(t *testing.T) { {name: "different range count", in: "expected at most 3 expressions", want: "expected at most 3 expressions"}, {name: "goto without position", in: "goto L jumps over variable declaration", want: "goto L jumps over variable declaration"}, {name: "non-declaration assignment", in: "cannot use x as int value in argument", want: "cannot use x as int value in argument"}, + {name: "invalid import quote", in: `non-canonical import path "\x": should be "good"`, want: `non-canonical import path "\x": should be "good"`}, + {name: "non-struct field", in: "unknown field value in map literal", want: "unknown field value in map literal"}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { diff --git a/test/goroot/xfail.yaml b/test/goroot/xfail.yaml index 39b30a73d6..bbb21bf369 100644 --- a/test/goroot/xfail.yaml +++ b/test/goroot/xfail.yaml @@ -1785,10 +1785,6 @@ xfails: directive: errorcheck case: nilptr3.go reason: gc-specific nil-check elimination diagnostics are not implemented by llgo - - version: go1.26 - directive: errorcheck - case: fixedbugs/issue11362.go - reason: llgo uses different non-canonical import-path diagnostic punctuation - version: go1.26 directive: errorcheck case: fixedbugs/issue23664.go @@ -1845,10 +1841,6 @@ xfails: directive: errorcheck case: syntax/semi7.go reason: llgo parser recovery emits additional semicolon and else diagnostics - - version: go1.26 - directive: errorcheck - case: fixedbugs/issue31053.dir/main.go - reason: llgo resolves the companion package but uses different unexported-field diagnostic wording - version: go1.26 directive: errorcheck case: escape4.go @@ -1945,10 +1937,6 @@ xfails: directive: errorcheck case: fixedbugs/issue10700.dir/test.go reason: gc-specific -m optimization diagnostics are not implemented by llgo - - version: go1.26 - directive: errorcheck - case: import6.go - reason: llgo loader does not emit the expected absolute-import-path diagnostic - version: go1.26 directive: errorcheck case: fixedbugs/issue13274.go @@ -1981,10 +1969,6 @@ xfails: directive: errorcheck case: fixedbugs/notinheap3.go reason: gc runtime write-barrier and notinheap diagnostics are not implemented by llgo - - version: go1.26 - directive: errorcheck - case: embedvers.go - reason: llgo loader reports an embed-pattern error before the expected language-version diagnostic - version: go1.26 directive: errorcheck case: fixedbugs/issue13273.go @@ -2001,10 +1985,6 @@ xfails: directive: errorcheck case: fixedbugs/notinheap2.go reason: gc-specific notinheap allocation diagnostics are not implemented by llgo - - version: go1.26 - directive: errorcheck - case: embedfunc.go - reason: llgo loader reports an embed-pattern error instead of the expected local-variable directive error - version: go1.26 directive: errorcheck case: fixedbugs/issue13266.go