From e2e2cb38be795b8b71561223ba8d1349ca58e3ad Mon Sep 17 00:00:00 2001 From: xushiwei Date: Sat, 26 Apr 2025 22:23:36 +0800 Subject: [PATCH] fix #965 --- cl/_testdefer/gobuild/in.go | 737 ++++++++++++++++++++++++++++++++++ cl/_testdefer/gobuild/out.txt | 206 ++++++++++ cl/blocks/block.go | 10 +- cl/blocks/block_test.go | 3 + 4 files changed, 955 insertions(+), 1 deletion(-) create mode 100644 cl/_testdefer/gobuild/in.go create mode 100644 cl/_testdefer/gobuild/out.txt diff --git a/cl/_testdefer/gobuild/in.go b/cl/_testdefer/gobuild/in.go new file mode 100644 index 00000000..72134b44 --- /dev/null +++ b/cl/_testdefer/gobuild/in.go @@ -0,0 +1,737 @@ +package main + +import ( + "errors" + "fmt" + "go/ast" + "go/doc" + "go/token" + "io/fs" + "os" + pathpkg "path" + "runtime" + "slices" + "strconv" + "strings" +) + +func IsLocalImport(path string) bool { + return true +} + +func isAbsPath(path string) bool { + return strings.HasPrefix(path, "/") +} + +func isDir(path string) bool { + fi, err := os.Stat(path) + if err != nil { + return false + } + return fi.IsDir() +} + +func isFile(path string) bool { + fi, err := os.Stat(path) + if err != nil { + return false + } + return fi.Mode().IsRegular() +} + +func joinPath(a string, b ...string) string { + if isAbsPath(b[0]) { + return b[0] + } + return pathpkg.Join(append([]string{a}, b...)...) +} + +func nameExt(path string) string { + return "" +} + +func gopath() []string { + all := make([]string, 0, 10) + for _, p := range strings.Split(os.Getenv("GOPATH"), ":") { + if p != "" { + all = append(all, p) + } + } + return all +} + +type Context struct { + InstallSuffix string + Compiler string + GOOS string + GOARCH string + GOROOT string + CgoEnabled bool +} + +type Package struct { + ImportPath string + Dir string + Goroot bool + Root string + ConflictDir string + SrcRoot string + PkgRoot string + BinDir string + PkgTargetRoot string + PkgObj string + InvalidGoFiles []string + IgnoredGoFiles []string + IgnoredOtherFiles []string + CgoFiles []string + XTestGoFiles []string + TestGoFiles []string + GoFiles []string + Directives []Directive + TestDirectives []Directive + XTestDirectives []Directive + BinaryOnly bool + Name string + Doc string + ImportComment string + AllTags []string + EmbedPatterns []string + TestEmbedPatterns []string + XTestEmbedPatterns []string + Imports []string + TestImports []string + XTestImports []string + EmbedPatternPos map[string][]token.Position + TestEmbedPatternPos map[string][]token.Position + XTestEmbedPatternPos map[string][]token.Position + ImportPos map[string][]token.Position + TestImportPos map[string][]token.Position + XTestImportPos map[string][]token.Position + SFiles []string +} + +type Directive struct { +} + +type MultiplePackageError struct { + Dir string + Packages []string + Files []string +} + +func (e *MultiplePackageError) Error() string { + return fmt.Sprintf("multiple packages in single directory: %s\n\t%s\n\t%s", e.Dir, strings.Join(e.Packages, "\n\t"), strings.Join(e.Files, "\n\t")) +} + +type ImportMode = uint + +const ( + IgnoreVendor ImportMode = 1 << iota + AllowBinary + FindOnly + ImportComment +) + +func importGo(ctx *Context, p *Package, path, srcDir string, mode ImportMode) error { + return nil +} + +func hasSubdir(root, sub string) (string, bool) { + return sub, true +} + +func hasGoFiles(ctxt *Context, file string) bool { + return true +} + +func isStandardImportPath(path string) bool { + return true +} + +func readDir(name string) ([]os.DirEntry, error) { + return nil, nil +} + +func findImportComment(data []byte) (s string, line int) { + return "", 0 +} + +func saveCgo(ctxt *Context, filename string, p *Package, doc *ast.CommentGroup) error { + return nil +} + +func cleanDecls(m map[string][]token.Position) ([]string, map[string][]token.Position) { + return nil, nil +} + +func fileListForExt(p *Package, ext string) *[]string { + return nil +} + +type fileInfo struct { + name string // full name including dir + header []byte + fset *token.FileSet + parsed *ast.File + parseErr error + imports []fileImport + embeds []fileEmbed + directives []Directive +} + +type fileImport struct { + path string + pos token.Pos + doc *ast.CommentGroup +} + +type fileEmbed struct { + pattern string + pos token.Position +} + +func matchFile(ctxt *Context, dir, name string, allTags map[string]bool, binaryOnly *bool, fset *token.FileSet) (*fileInfo, error) { + return nil, nil +} + +var errNoModules = errors.New("no modules") + +type godebug struct { + name string +} + +func NewGodebug(name string) *godebug { + return &godebug{ + name: name, + } +} + +func (g *godebug) IncNonDefault() { +} + +func (g *godebug) Value() string { + return g.name +} + +var installgoroot = NewGodebug("installgoroot") + +func IsStandardPackage(a, b, c string) bool { + return true +} + +type NoGoError struct { + Dir string +} + +func (e *NoGoError) Error() string { + return "no Go files in " + e.Dir +} + +func Import(ctxt *Context, path string, srcDir string, mode ImportMode) (*Package, error) { + p := &Package{ + ImportPath: path, + } + if path == "" { + return p, fmt.Errorf("import %q: invalid import path", path) + } + + var pkgtargetroot string + var pkga string + var pkgerr error + suffix := "" + if ctxt.InstallSuffix != "" { + suffix = "_" + ctxt.InstallSuffix + } + switch ctxt.Compiler { + case "gccgo": + pkgtargetroot = "pkg/gccgo_" + ctxt.GOOS + "_" + ctxt.GOARCH + suffix + case "gc": + pkgtargetroot = "pkg/" + ctxt.GOOS + "_" + ctxt.GOARCH + suffix + default: + // Save error for end of function. + pkgerr = fmt.Errorf("import %q: unknown compiler %q", path, ctxt.Compiler) + } + setPkga := func() { + switch ctxt.Compiler { + case "gccgo": + dir, elem := pathpkg.Split(p.ImportPath) + pkga = pkgtargetroot + "/" + dir + "lib" + elem + ".a" + case "gc": + pkga = pkgtargetroot + "/" + p.ImportPath + ".a" + } + } + setPkga() + + binaryOnly := false + if IsLocalImport(path) { + pkga = "" // local imports have no installed path + if srcDir == "" { + return p, fmt.Errorf("import %q: import relative to unknown directory", path) + } + if !isAbsPath(path) { + p.Dir = joinPath(srcDir, path) + } + // p.Dir directory may or may not exist. Gather partial information first, check if it exists later. + // Determine canonical import path, if any. + // Exclude results where the import path would include /testdata/. + inTestdata := func(sub string) bool { + return strings.Contains(sub, "/testdata/") || strings.HasSuffix(sub, "/testdata") || strings.HasPrefix(sub, "testdata/") || sub == "testdata" + } + if ctxt.GOROOT != "" { + root := joinPath(runtime.GOROOT(), "src") + if sub, ok := hasSubdir(root, p.Dir); ok && !inTestdata(sub) { + p.Goroot = true + p.ImportPath = sub + p.Root = ctxt.GOROOT + setPkga() // p.ImportPath changed + goto Found + } + } + all := gopath() + for i, root := range all { + rootsrc := joinPath(root, "src") + if sub, ok := hasSubdir(rootsrc, p.Dir); ok && !inTestdata(sub) { + // We found a potential import path for dir, + // but check that using it wouldn't find something + // else first. + if runtime.GOROOT() != "" && ctxt.Compiler != "gccgo" { + if dir := joinPath(runtime.GOROOT(), "src", sub); isDir(dir) { + p.ConflictDir = dir + goto Found + } + } + for _, earlyRoot := range all[:i] { + if dir := joinPath(earlyRoot, "src", sub); isDir(dir) { + p.ConflictDir = dir + goto Found + } + } + + // sub would not name some other directory instead of this one. + // Record it. + p.ImportPath = sub + p.Root = root + setPkga() // p.ImportPath changed + goto Found + } + } + // It's okay that we didn't find a root containing dir. + // Keep going with the information we have. + } else { + if strings.HasPrefix(path, "/") { + return p, fmt.Errorf("import %q: cannot import absolute path", path) + } + + if err := importGo(ctxt, p, path, srcDir, mode); err == nil { + goto Found + } else if err != errNoModules { + return p, err + } + + gopath := gopath() // needed twice below; avoid computing many times + + // tried records the location of unsuccessful package lookups + var tried struct { + vendor []string + goroot string + gopath []string + } + + // Vendor directories get first chance to satisfy import. + if mode&IgnoreVendor == 0 && srcDir != "" { + searchVendor := func(root string, isGoroot bool) bool { + sub, ok := hasSubdir(root, srcDir) + if !ok || !strings.HasPrefix(sub, "src/") || strings.Contains(sub, "/testdata/") { + return false + } + for { + vendor := joinPath(root, sub, "vendor") + if isDir(vendor) { + dir := joinPath(vendor, path) + if isDir(dir) && hasGoFiles(ctxt, dir) { + p.Dir = dir + p.ImportPath = strings.TrimPrefix(pathpkg.Join(sub, "vendor", path), "src/") + p.Goroot = isGoroot + p.Root = root + setPkga() // p.ImportPath changed + return true + } + tried.vendor = append(tried.vendor, dir) + } + i := strings.LastIndex(sub, "/") + if i < 0 { + break + } + sub = sub[:i] + } + return false + } + if ctxt.Compiler != "gccgo" && ctxt.GOROOT != "" && searchVendor(ctxt.GOROOT, true) { + goto Found + } + for _, root := range gopath { + if searchVendor(root, false) { + goto Found + } + } + } + + // Determine directory from import path. + if ctxt.GOROOT != "" { + // If the package path starts with "vendor/", only search GOROOT before + // GOPATH if the importer is also within GOROOT. That way, if the user has + // vendored in a package that is subsequently included in the standard + // distribution, they'll continue to pick up their own vendored copy. + gorootFirst := srcDir == "" || !strings.HasPrefix(path, "vendor/") + if !gorootFirst { + _, gorootFirst = hasSubdir(runtime.GOROOT(), srcDir) + } + if gorootFirst { + dir := joinPath(runtime.GOROOT(), "src", path) + if ctxt.Compiler != "gccgo" { + isDir := isDir(dir) + binaryOnly = !isDir && mode&AllowBinary != 0 && pkga != "" && isFile(joinPath(runtime.GOROOT(), pkga)) + if isDir || binaryOnly { + p.Dir = dir + p.Goroot = true + p.Root = runtime.GOROOT() + goto Found + } + } + tried.goroot = dir + } + if ctxt.Compiler == "gccgo" && IsStandardPackage(runtime.GOROOT(), ctxt.Compiler, path) { + // TODO(bcmills): Setting p.Dir here is misleading, because gccgo + // doesn't actually load its standard-library packages from this + // directory. See if we can leave it unset. + p.Dir = joinPath(runtime.GOROOT(), "src", path) + p.Goroot = true + p.Root = runtime.GOROOT() + goto Found + } + } + for _, root := range gopath { + dir := joinPath(root, "src", path) + isDir := isDir(dir) + binaryOnly = !isDir && mode&AllowBinary != 0 && pkga != "" && isFile(joinPath(root, pkga)) + if isDir || binaryOnly { + p.Dir = dir + p.Root = root + goto Found + } + tried.gopath = append(tried.gopath, dir) + } + + // If we tried GOPATH first due to a "vendor/" prefix, fall back to GOPATH. + // That way, the user can still get useful results from 'go list' for + // standard-vendored paths passed on the command line. + if runtime.GOROOT() != "" && tried.goroot == "" { + dir := joinPath(runtime.GOROOT(), "src", path) + if ctxt.Compiler != "gccgo" { + isDir := isDir(dir) + binaryOnly = !isDir && mode&AllowBinary != 0 && pkga != "" && isFile(joinPath(runtime.GOROOT(), pkga)) + if isDir || binaryOnly { + p.Dir = dir + p.Goroot = true + p.Root = runtime.GOROOT() + goto Found + } + } + tried.goroot = dir + } + + // package was not found + var paths []string + format := "\t%s (vendor tree)" + for _, dir := range tried.vendor { + paths = append(paths, fmt.Sprintf(format, dir)) + format = "\t%s" + } + if tried.goroot != "" { + paths = append(paths, fmt.Sprintf("\t%s (from $GOROOT)", tried.goroot)) + } else { + paths = append(paths, "\t($GOROOT not set)") + } + format = "\t%s (from $GOPATH)" + for _, dir := range tried.gopath { + paths = append(paths, fmt.Sprintf(format, dir)) + format = "\t%s" + } + if len(tried.gopath) == 0 { + paths = append(paths, "\t($GOPATH not set. For more details see: 'go help gopath')") + } + return p, fmt.Errorf("cannot find package %q in any of:\n%s", path, strings.Join(paths, "\n")) + } + +Found: + if p.Root != "" { + p.SrcRoot = joinPath(p.Root, "src") + p.PkgRoot = joinPath(p.Root, "pkg") + p.BinDir = joinPath(p.Root, "bin") + if pkga != "" { + // Always set PkgTargetRoot. It might be used when building in shared + // mode. + p.PkgTargetRoot = joinPath(p.Root, pkgtargetroot) + + // Set the install target if applicable. + if !p.Goroot || (installgoroot.Value() == "all" && p.ImportPath != "unsafe" && p.ImportPath != "builtin") { + if p.Goroot { + installgoroot.IncNonDefault() + } + p.PkgObj = joinPath(p.Root, pkga) + } + } + } + + // If it's a local import path, by the time we get here, we still haven't checked + // that p.Dir directory exists. This is the right time to do that check. + // We can't do it earlier, because we want to gather partial information for the + // non-nil *Package returned when an error occurs. + // We need to do this before we return early on FindOnly flag. + if IsLocalImport(path) && !isDir(p.Dir) { + if ctxt.Compiler == "gccgo" && p.Goroot { + // gccgo has no sources for GOROOT packages. + return p, nil + } + + // package was not found + return p, fmt.Errorf("cannot find package %q in:\n\t%s", p.ImportPath, p.Dir) + } + + if mode&FindOnly != 0 { + return p, pkgerr + } + if binaryOnly && (mode&AllowBinary) != 0 { + return p, pkgerr + } + + if ctxt.Compiler == "gccgo" && p.Goroot { + // gccgo has no sources for GOROOT packages. + return p, nil + } + + dirs, err := readDir(p.Dir) + if err != nil { + return p, err + } + + var badGoError error + badGoFiles := make(map[string]bool) + badGoFile := func(name string, err error) { + if badGoError == nil { + badGoError = err + } + if !badGoFiles[name] { + p.InvalidGoFiles = append(p.InvalidGoFiles, name) + badGoFiles[name] = true + } + } + + var Sfiles []string // files with ".S"(capital S)/.sx(capital s equivalent for case insensitive filesystems) + var firstFile, firstCommentFile string + embedPos := make(map[string][]token.Position) + testEmbedPos := make(map[string][]token.Position) + xTestEmbedPos := make(map[string][]token.Position) + importPos := make(map[string][]token.Position) + testImportPos := make(map[string][]token.Position) + xTestImportPos := make(map[string][]token.Position) + allTags := make(map[string]bool) + fset := token.NewFileSet() + for _, d := range dirs { + if d.IsDir() { + continue + } + if d.Type() == fs.ModeSymlink { + if isDir(joinPath(p.Dir, d.Name())) { + // Symlinks to directories are not source files. + continue + } + } + + name := d.Name() + ext := nameExt(name) + + info, err := matchFile(ctxt, p.Dir, name, allTags, &p.BinaryOnly, fset) + if err != nil && strings.HasSuffix(name, ".go") { + badGoFile(name, err) + continue + } + if info == nil { + if strings.HasPrefix(name, "_") || strings.HasPrefix(name, ".") { + // not due to build constraints - don't report + } else if ext == ".go" { + p.IgnoredGoFiles = append(p.IgnoredGoFiles, name) + } else if fileListForExt(p, ext) != nil { + p.IgnoredOtherFiles = append(p.IgnoredOtherFiles, name) + } + continue + } + + // Going to save the file. For non-Go files, can stop here. + switch ext { + case ".go": + // keep going + case ".S", ".sx": + // special case for cgo, handled at end + Sfiles = append(Sfiles, name) + continue + default: + if list := fileListForExt(p, ext); list != nil { + *list = append(*list, name) + } + continue + } + + data, filename := info.header, info.name + + if info.parseErr != nil { + badGoFile(name, info.parseErr) + // Fall through: we might still have a partial AST in info.parsed, + // and we want to list files with parse errors anyway. + } + + var pkg string + if info.parsed != nil { + pkg = info.parsed.Name.Name + if pkg == "documentation" { + p.IgnoredGoFiles = append(p.IgnoredGoFiles, name) + continue + } + } + + isTest := strings.HasSuffix(name, "_test.go") + isXTest := false + if isTest && strings.HasSuffix(pkg, "_test") && p.Name != pkg { + isXTest = true + pkg = pkg[:len(pkg)-len("_test")] + } + + if p.Name == "" { + p.Name = pkg + firstFile = name + } else if pkg != p.Name { + // TODO(#45999): The choice of p.Name is arbitrary based on file iteration + // order. Instead of resolving p.Name arbitrarily, we should clear out the + // existing name and mark the existing files as also invalid. + badGoFile(name, &MultiplePackageError{ + Dir: p.Dir, + Packages: []string{p.Name, pkg}, + Files: []string{firstFile, name}, + }) + } + // Grab the first package comment as docs, provided it is not from a test file. + if info.parsed != nil && info.parsed.Doc != nil && p.Doc == "" && !isTest && !isXTest { + p.Doc = doc.Synopsis(info.parsed.Doc.Text()) + } + + if mode&ImportComment != 0 { + qcom, line := findImportComment(data) + if line != 0 { + com, err := strconv.Unquote(qcom) + if err != nil { + badGoFile(name, fmt.Errorf("%s:%d: cannot parse import comment", filename, line)) + } else if p.ImportComment == "" { + p.ImportComment = com + firstCommentFile = name + } else if p.ImportComment != com { + badGoFile(name, fmt.Errorf("found import comments %q (%s) and %q (%s) in %s", p.ImportComment, firstCommentFile, com, name, p.Dir)) + } + } + } + + // Record imports and information about cgo. + isCgo := false + for _, imp := range info.imports { + if imp.path == "C" { + if isTest { + badGoFile(name, fmt.Errorf("use of cgo in test %s not supported", filename)) + continue + } + isCgo = true + if imp.doc != nil { + if err := saveCgo(ctxt, filename, p, imp.doc); err != nil { + badGoFile(name, err) + } + } + } + } + + var fileList *[]string + var importMap, embedMap map[string][]token.Position + var directives *[]Directive + switch { + case isCgo: + allTags["cgo"] = true + if ctxt.CgoEnabled { + fileList = &p.CgoFiles + importMap = importPos + embedMap = embedPos + directives = &p.Directives + } else { + // Ignore imports and embeds from cgo files if cgo is disabled. + fileList = &p.IgnoredGoFiles + } + case isXTest: + fileList = &p.XTestGoFiles + importMap = xTestImportPos + embedMap = xTestEmbedPos + directives = &p.XTestDirectives + case isTest: + fileList = &p.TestGoFiles + importMap = testImportPos + embedMap = testEmbedPos + directives = &p.TestDirectives + default: + fileList = &p.GoFiles + importMap = importPos + embedMap = embedPos + directives = &p.Directives + } + *fileList = append(*fileList, name) + if importMap != nil { + for _, imp := range info.imports { + importMap[imp.path] = append(importMap[imp.path], fset.Position(imp.pos)) + } + } + if embedMap != nil { + for _, emb := range info.embeds { + embedMap[emb.pattern] = append(embedMap[emb.pattern], emb.pos) + } + } + if directives != nil { + *directives = append(*directives, info.directives...) + } + } + + for tag := range allTags { + p.AllTags = append(p.AllTags, tag) + } + slices.Sort(p.AllTags) + + p.EmbedPatterns, p.EmbedPatternPos = cleanDecls(embedPos) + p.TestEmbedPatterns, p.TestEmbedPatternPos = cleanDecls(testEmbedPos) + p.XTestEmbedPatterns, p.XTestEmbedPatternPos = cleanDecls(xTestEmbedPos) + + p.Imports, p.ImportPos = cleanDecls(importPos) + p.TestImports, p.TestImportPos = cleanDecls(testImportPos) + p.XTestImports, p.XTestImportPos = cleanDecls(xTestImportPos) + + // add the .S/.sx files only if we are using cgo + // (which means gcc will compile them). + // The standard assemblers expect .s files. + if len(p.CgoFiles) > 0 { + p.SFiles = append(p.SFiles, Sfiles...) + slices.Sort(p.SFiles) + } else { + p.IgnoredOtherFiles = append(p.IgnoredOtherFiles, Sfiles...) + slices.Sort(p.IgnoredOtherFiles) + } + + if badGoError != nil { + return p, badGoError + } + if len(p.GoFiles)+len(p.CgoFiles)+len(p.TestGoFiles)+len(p.XTestGoFiles) == 0 { + return p, &NoGoError{p.Dir} + } + return p, pkgerr +} diff --git a/cl/_testdefer/gobuild/out.txt b/cl/_testdefer/gobuild/out.txt new file mode 100644 index 00000000..1eecc0e4 --- /dev/null +++ b/cl/_testdefer/gobuild/out.txt @@ -0,0 +1,206 @@ + 0: always + 1: cond + 2: cond + 3: cond + 4: cond + 6: cond + 8: cond + 7: cond + 9: cond + 5: cond +10: cond +11: cond +12: cond +13: cond +33: cond +34: cond +14: cond +35: cond +15: cond +36: cond +37: cond +16: cond +40: cond +19: cond +38: cond +17: cond +18: cond +43: cond +42: cond +41: cond +21: loop +22: loop +24: loop +23: cond +27: cond +25: cond +28: cond +26: cond +29: loop +30: loop +31: cond +32: cond +44: loop +45: loop +39: cond +46: cond +48: cond +49: cond +50: cond +51: cond +52: cond +54: cond +59: cond +58: cond +56: cond +57: cond +61: cond +60: cond +55: cond +53: cond +63: cond +62: cond +47: cond +64: loop +65: loop +68: loop +73: loop +72: loop +66: cond +70: cond +71: cond +76: cond +69: cond +74: cond +67: cond +77: cond +82: cond +81: cond +79: cond +80: cond +84: cond +83: cond +78: cond +20: cond +75: cond +96: cond +98: cond +100: cond +102: cond +101: cond +99: cond +103: cond +104: cond +97: cond +107: cond +106: cond +105: cond +111: cond +112: cond +110: cond +115: cond +108: cond +109: cond +113: cond +114: cond +118: cond +116: cond +117: cond +119: cond +120: cond +85: loop +86: loop +87: cond +88: cond +90: cond +89: cond +91: loop +92: loop +93: cond +94: cond +95: cond +121: loop +122: loop +123: cond +124: cond +125: cond +126: cond +129: cond +127: cond +128: cond +130: cond +131: cond +133: cond +137: cond +139: cond +132: cond +143: cond +140: cond +134: cond +135: cond +144: cond +138: cond +141: cond +136: cond +145: cond +142: cond +147: cond +146: cond +151: cond +150: cond +148: cond +149: cond +152: cond +154: cond +155: cond +153: cond +161: cond +160: cond +159: cond +158: cond +156: cond +157: cond +162: cond +164: cond +165: cond +166: cond +167: cond +168: cond +169: cond +163: cond +170: loop +171: loop +173: loop +174: loop +172: cond +175: cond +179: cond +181: cond +176: cond +182: cond +183: cond +180: cond +185: cond +177: cond +184: cond +186: cond +178: cond +187: cond +189: loop +190: loop +188: cond +191: cond +193: loop +194: loop +192: cond +195: cond +196: loop +197: loop +198: cond +199: cond +201: cond +200: cond +202: cond +203: cond +204: cond +205: cond diff --git a/cl/blocks/block.go b/cl/blocks/block.go index 4f220339..6dda4d62 100644 --- a/cl/blocks/block.go +++ b/cl/blocks/block.go @@ -60,11 +60,14 @@ func findLoop(states []*blockState, path []int, from, iblk int) []int { path = append(path, iblk) self := states[iblk] for _, succ := range self.succs { - if states[succ].fdel { + if s := states[succ]; s.fdel || s.loop { continue } if pos := find(path, succ); pos >= 0 { if pos > 0 { + for _, i := range path[pos:] { // mark inner loop + states[i].loop = true + } continue } for _, i := range path { @@ -72,6 +75,11 @@ func findLoop(states []*blockState, path []int, from, iblk int) []int { s.loop = true s.fdel = true } + for _, s := range states { // clear inner loop mark + if !s.fdel { + s.loop = false + } + } return path } if ret := findLoop(states, path, from, succ); len(ret) > 0 { diff --git a/cl/blocks/block_test.go b/cl/blocks/block_test.go index 1a8b423b..df26454c 100644 --- a/cl/blocks/block_test.go +++ b/cl/blocks/block_test.go @@ -44,6 +44,9 @@ func TestTestdefer(t *testing.T) { if strings.HasPrefix(name, "firstloop") { return "Loop" } + if strings.HasPrefix(name, "gobuild") { + return "Import" + } return "main" }) }