github.com/vanstinator/golangci-lint@v0.0.0-20240223191551-cc572f00d9d1/pkg/packages/util.go (about) 1 package packages 2 3 import ( 4 "fmt" 5 "regexp" 6 "strings" 7 8 "golang.org/x/tools/go/packages" 9 ) 10 11 // reFile matches a line who starts with path and position. 12 // ex: `/example/main.go:11:17: foobar` 13 var reFile = regexp.MustCompile(`^.+\.go:\d+:\d+: .+`) 14 15 func ExtractErrors(pkg *packages.Package) []packages.Error { 16 errors := extractErrorsImpl(pkg, map[*packages.Package]bool{}) 17 if len(errors) == 0 { 18 return errors 19 } 20 21 seenErrors := map[string]bool{} 22 var uniqErrors []packages.Error 23 for _, err := range errors { 24 msg := stackCrusher(err.Error()) 25 if seenErrors[msg] { 26 continue 27 } 28 29 if msg != err.Error() { 30 continue 31 } 32 33 seenErrors[msg] = true 34 35 uniqErrors = append(uniqErrors, err) 36 } 37 38 if len(pkg.GoFiles) != 0 { 39 // errors were extracted from deps and have at least one file in package 40 for i := range uniqErrors { 41 if _, parseErr := ParseErrorPosition(uniqErrors[i].Pos); parseErr == nil { 42 continue 43 } 44 45 // change pos to local file to properly process it by processors (properly read line etc.) 46 uniqErrors[i].Msg = fmt.Sprintf("%s: %s", uniqErrors[i].Pos, uniqErrors[i].Msg) 47 uniqErrors[i].Pos = fmt.Sprintf("%s:1", pkg.GoFiles[0]) 48 } 49 50 // some errors like "code in directory expects import" don't have Pos, set it here 51 for i := range uniqErrors { 52 err := &uniqErrors[i] 53 if err.Pos == "" { 54 err.Pos = fmt.Sprintf("%s:1", pkg.GoFiles[0]) 55 } 56 } 57 } 58 59 return uniqErrors 60 } 61 62 func extractErrorsImpl(pkg *packages.Package, seenPackages map[*packages.Package]bool) []packages.Error { 63 if seenPackages[pkg] { 64 return nil 65 } 66 seenPackages[pkg] = true 67 68 if !pkg.IllTyped { // otherwise, it may take hours to traverse all deps many times 69 return nil 70 } 71 72 if len(pkg.Errors) > 0 { 73 return pkg.Errors 74 } 75 76 var errors []packages.Error 77 for _, iPkg := range pkg.Imports { 78 iPkgErrors := extractErrorsImpl(iPkg, seenPackages) 79 if iPkgErrors != nil { 80 errors = append(errors, iPkgErrors...) 81 } 82 } 83 84 return errors 85 } 86 87 func stackCrusher(msg string) string { 88 index := strings.Index(msg, "(") 89 lastIndex := strings.LastIndex(msg, ")") 90 91 if index == -1 || index == len(msg)-1 || lastIndex == -1 || lastIndex != len(msg)-1 { 92 return msg 93 } 94 95 frag := msg[index+1 : lastIndex] 96 97 if !reFile.MatchString(frag) { 98 return msg 99 } 100 101 return stackCrusher(frag) 102 }