github.com/amarpal/go-tools@v0.0.0-20240422043104-40142f59f616/stylecheck/st1000/st1000.go (about) 1 package st1000 2 3 import ( 4 "fmt" 5 "go/ast" 6 "strings" 7 8 "github.com/amarpal/go-tools/analysis/code" 9 "github.com/amarpal/go-tools/analysis/lint" 10 "github.com/amarpal/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.Documentation{ 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 }