github.com/shulhan/golangci-lint@v1.10.1/pkg/golinters/megacheck.go (about)

     1  package golinters
     2  
     3  import (
     4  	"context"
     5  	"errors"
     6  	"fmt"
     7  	"strings"
     8  
     9  	"golang.org/x/tools/go/loader"
    10  	"golang.org/x/tools/go/ssa"
    11  	"honnef.co/go/tools/lint"
    12  	"honnef.co/go/tools/lint/lintutil"
    13  	"honnef.co/go/tools/simple"
    14  	"honnef.co/go/tools/staticcheck"
    15  	"honnef.co/go/tools/unused"
    16  
    17  	"github.com/golangci/golangci-lint/pkg/fsutils"
    18  	"github.com/golangci/golangci-lint/pkg/lint/linter"
    19  	"github.com/golangci/golangci-lint/pkg/result"
    20  )
    21  
    22  type Megacheck struct {
    23  	UnusedEnabled      bool
    24  	GosimpleEnabled    bool
    25  	StaticcheckEnabled bool
    26  }
    27  
    28  func (m Megacheck) Name() string {
    29  	names := []string{}
    30  	if m.UnusedEnabled {
    31  		names = append(names, "unused")
    32  	}
    33  	if m.GosimpleEnabled {
    34  		names = append(names, "gosimple")
    35  	}
    36  	if m.StaticcheckEnabled {
    37  		names = append(names, "staticcheck")
    38  	}
    39  
    40  	if len(names) == 1 {
    41  		return names[0] // only one sublinter is enabled
    42  	}
    43  
    44  	if len(names) == 3 {
    45  		return "megacheck" // all enabled
    46  	}
    47  
    48  	return fmt.Sprintf("megacheck.{%s}", strings.Join(names, ","))
    49  }
    50  
    51  func (m Megacheck) Desc() string {
    52  	descs := map[string]string{
    53  		"unused":      "Checks Go code for unused constants, variables, functions and types",
    54  		"gosimple":    "Linter for Go source code that specializes in simplifying a code",
    55  		"staticcheck": "Staticcheck is a go vet on steroids, applying a ton of static analysis checks",
    56  		"megacheck":   "3 sub-linters in one: unused, gosimple and staticcheck",
    57  	}
    58  
    59  	return descs[m.Name()]
    60  }
    61  
    62  func prettifyCompilationError(err error) error {
    63  	i, _ := TypeCheck{}.parseError(err)
    64  	if i == nil {
    65  		return err
    66  	}
    67  
    68  	shortFilename, pathErr := fsutils.ShortestRelPath(i.Pos.Filename, "")
    69  	if pathErr != nil {
    70  		return err
    71  	}
    72  
    73  	errText := shortFilename
    74  	if i.Line() != 0 {
    75  		errText += fmt.Sprintf(":%d", i.Line())
    76  	}
    77  	errText += fmt.Sprintf(": %s", i.Text)
    78  	return errors.New(errText)
    79  }
    80  
    81  func (m Megacheck) Run(ctx context.Context, lintCtx *linter.Context) ([]result.Issue, error) {
    82  	if len(lintCtx.NotCompilingPackages) != 0 {
    83  		var packages []string
    84  		var errors []error
    85  		for _, p := range lintCtx.NotCompilingPackages {
    86  			packages = append(packages, p.String())
    87  			errors = append(errors, p.Errors...)
    88  		}
    89  
    90  		warnText := fmt.Sprintf("Can't run megacheck because of compilation errors in packages %s",
    91  			packages)
    92  		if len(errors) != 0 {
    93  			warnText += fmt.Sprintf(": %s", prettifyCompilationError(errors[0]))
    94  			if len(errors) > 1 {
    95  				const runCmd = "golangci-lint run --no-config --disable-all -E typecheck"
    96  				warnText += fmt.Sprintf(" and %d more errors: run `%s` to see all errors", len(errors)-1, runCmd)
    97  			}
    98  		}
    99  		lintCtx.Log.Warnf("%s", warnText)
   100  
   101  		// megacheck crashes if there are not compiling packages
   102  		return nil, nil
   103  	}
   104  
   105  	issues := runMegacheck(lintCtx.Program, lintCtx.SSAProgram, lintCtx.LoaderConfig,
   106  		m.StaticcheckEnabled, m.GosimpleEnabled, m.UnusedEnabled, lintCtx.Settings().Unused.CheckExported)
   107  	if len(issues) == 0 {
   108  		return nil, nil
   109  	}
   110  
   111  	res := make([]result.Issue, 0, len(issues))
   112  	for _, i := range issues {
   113  		res = append(res, result.Issue{
   114  			Pos:        i.Position,
   115  			Text:       markIdentifiers(i.Text),
   116  			FromLinter: m.Name(),
   117  		})
   118  	}
   119  	return res, nil
   120  }
   121  
   122  func runMegacheck(program *loader.Program, ssaProg *ssa.Program, conf *loader.Config,
   123  	enableStaticcheck, enableGosimple, enableUnused, checkExportedUnused bool) []lint.Problem {
   124  
   125  	var checkers []lintutil.CheckerConfig
   126  
   127  	if enableStaticcheck {
   128  		sac := staticcheck.NewChecker()
   129  		checkers = append(checkers, lintutil.CheckerConfig{
   130  			Checker: sac,
   131  		})
   132  	}
   133  
   134  	if enableGosimple {
   135  		sc := simple.NewChecker()
   136  		checkers = append(checkers, lintutil.CheckerConfig{
   137  			Checker: sc,
   138  		})
   139  	}
   140  
   141  	if enableUnused {
   142  		uc := unused.NewChecker(unused.CheckAll)
   143  		uc.WholeProgram = checkExportedUnused
   144  		uc.ConsiderReflection = true
   145  		checkers = append(checkers, lintutil.CheckerConfig{
   146  			Checker: unused.NewLintChecker(uc),
   147  		})
   148  	}
   149  
   150  	fs := lintutil.FlagSet("megacheck")
   151  	return lintutil.ProcessFlagSet(checkers, fs, program, ssaProg, conf)
   152  }