st1000.go raw

   1  package st1000
   2  
   3  import (
   4  	"fmt"
   5  	"go/ast"
   6  	"strings"
   7  
   8  	"honnef.co/go/tools/analysis/code"
   9  	"honnef.co/go/tools/analysis/lint"
  10  	"honnef.co/go/tools/analysis/report"
  11  
  12  	"golang.org/x/tools/go/analysis"
  13  )
  14  
  15  var SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{
  16  	Analyzer: &analysis.Analyzer{
  17  		Name: "ST1000",
  18  		Run:  run,
  19  	},
  20  	Doc: &lint.RawDocumentation{
  21  		Title: `Incorrect or missing package comment`,
  22  		Text: `Packages must have a package comment that is formatted according to
  23  the guidelines laid out in
  24  https://go.dev/wiki/CodeReviewComments#package-comments.`,
  25  		Since:      "2019.1",
  26  		NonDefault: true,
  27  		MergeIf:    lint.MergeIfAny,
  28  	},
  29  })
  30  
  31  var Analyzer = SCAnalyzer.Analyzer
  32  
  33  func run(pass *analysis.Pass) (interface{}, error) {
  34  	// - At least one file in a non-main package should have a package comment
  35  	//
  36  	// - The comment should be of the form
  37  	// "Package x ...". This has a slight potential for false
  38  	// positives, as multiple files can have package comments, in
  39  	// which case they get appended. But that doesn't happen a lot in
  40  	// the real world.
  41  
  42  	if pass.Pkg.Name() == "main" {
  43  		return nil, nil
  44  	}
  45  	hasDocs := false
  46  	for _, f := range pass.Files {
  47  		if code.IsInTest(pass, f) {
  48  			continue
  49  		}
  50  		text, ok := docText(f.Doc)
  51  		if ok {
  52  			hasDocs = true
  53  			prefix := "Package " + f.Name.Name
  54  			isNonAlpha := func(b byte) bool {
  55  				// This check only considers ASCII, which can lead to false negatives for some malformed package
  56  				// comments.
  57  				return !((b >= '0' && b <= '9') || (b >= 'a' && b <= 'z') || (b >= 'A' && b <= 'Z'))
  58  			}
  59  			if !strings.HasPrefix(text, prefix) || (len(text) > len(prefix) && !isNonAlpha(text[len(prefix)])) {
  60  				report.Report(pass, f.Doc, fmt.Sprintf(`package comment should be of the form "%s..."`, prefix))
  61  			}
  62  		}
  63  	}
  64  
  65  	if !hasDocs {
  66  		for _, f := range pass.Files {
  67  			if code.IsInTest(pass, f) {
  68  				continue
  69  			}
  70  			report.Report(pass, f, "at least one file in a package should have a package comment", report.ShortRange())
  71  		}
  72  	}
  73  	return nil, nil
  74  }
  75  
  76  func docText(doc *ast.CommentGroup) (string, bool) {
  77  	if doc == nil {
  78  		return "", false
  79  	}
  80  	// We trim spaces primarily because of /**/ style comments, which often have leading space.
  81  	text := strings.TrimSpace(doc.Text())
  82  	return text, text != ""
  83  }
  84