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  }