github.com/Johnny2210/revive@v1.0.8-0.20210625134200-febf37ccd0f5/rule/package-comments.go (about)

     1  package rule
     2  
     3  import (
     4  	"fmt"
     5  	"go/ast"
     6  	"go/token"
     7  	"strings"
     8  
     9  	"github.com/mgechev/revive/lint"
    10  )
    11  
    12  // PackageCommentsRule lints the package comments. It complains if
    13  // there is no package comment, or if it is not of the right form.
    14  // This has a notable false positive in that a package comment
    15  // could rightfully appear in a different file of the same package,
    16  // but that's not easy to fix since this linter is file-oriented.
    17  type PackageCommentsRule struct{}
    18  
    19  // Apply applies the rule to given file.
    20  func (r *PackageCommentsRule) Apply(file *lint.File, _ lint.Arguments) []lint.Failure {
    21  	var failures []lint.Failure
    22  
    23  	if isTest(file) {
    24  		return failures
    25  	}
    26  
    27  	onFailure := func(failure lint.Failure) {
    28  		failures = append(failures, failure)
    29  	}
    30  
    31  	fileAst := file.AST
    32  	w := &lintPackageComments{fileAst, file, onFailure}
    33  	ast.Walk(w, fileAst)
    34  	return failures
    35  }
    36  
    37  // Name returns the rule name.
    38  func (r *PackageCommentsRule) Name() string {
    39  	return "package-comments"
    40  }
    41  
    42  type lintPackageComments struct {
    43  	fileAst   *ast.File
    44  	file      *lint.File
    45  	onFailure func(lint.Failure)
    46  }
    47  
    48  func (l *lintPackageComments) Visit(_ ast.Node) ast.Visitor {
    49  	if l.file.IsTest() {
    50  		return nil
    51  	}
    52  
    53  	const ref = styleGuideBase + "#package-comments"
    54  	prefix := "Package " + l.fileAst.Name.Name + " "
    55  
    56  	// Look for a detached package comment.
    57  	// First, scan for the last comment that occurs before the "package" keyword.
    58  	var lastCG *ast.CommentGroup
    59  	for _, cg := range l.fileAst.Comments {
    60  		if cg.Pos() > l.fileAst.Package {
    61  			// Gone past "package" keyword.
    62  			break
    63  		}
    64  		lastCG = cg
    65  	}
    66  	if lastCG != nil && strings.HasPrefix(lastCG.Text(), prefix) {
    67  		endPos := l.file.ToPosition(lastCG.End())
    68  		pkgPos := l.file.ToPosition(l.fileAst.Package)
    69  		if endPos.Line+1 < pkgPos.Line {
    70  			// There isn't a great place to anchor this error;
    71  			// the start of the blank lines between the doc and the package statement
    72  			// is at least pointing at the location of the problem.
    73  			pos := token.Position{
    74  				Filename: endPos.Filename,
    75  				// Offset not set; it is non-trivial, and doesn't appear to be needed.
    76  				Line:   endPos.Line + 1,
    77  				Column: 1,
    78  			}
    79  			l.onFailure(lint.Failure{
    80  				Category: "comments",
    81  				Position: lint.FailurePosition{
    82  					Start: pos,
    83  					End:   pos,
    84  				},
    85  				Confidence: 0.9,
    86  				Failure:    "package comment is detached; there should be no blank lines between it and the package statement",
    87  			})
    88  			return nil
    89  		}
    90  	}
    91  
    92  	if l.fileAst.Doc == nil {
    93  		l.onFailure(lint.Failure{
    94  			Category:   "comments",
    95  			Node:       l.fileAst,
    96  			Confidence: 0.2,
    97  			Failure:    "should have a package comment, unless it's in another file for this package",
    98  		})
    99  		return nil
   100  	}
   101  	s := l.fileAst.Doc.Text()
   102  	if ts := strings.TrimLeft(s, " \t"); ts != s {
   103  		l.onFailure(lint.Failure{
   104  			Category:   "comments",
   105  			Node:       l.fileAst.Doc,
   106  			Confidence: 1,
   107  			Failure:    "package comment should not have leading space",
   108  		})
   109  		s = ts
   110  	}
   111  	// Only non-main packages need to keep to this form.
   112  	if !l.file.Pkg.IsMain() && !strings.HasPrefix(s, prefix) {
   113  		l.onFailure(lint.Failure{
   114  			Category:   "comments",
   115  			Node:       l.fileAst.Doc,
   116  			Confidence: 1,
   117  			Failure:    fmt.Sprintf(`package comment should be of the form "%s..."`, prefix),
   118  		})
   119  	}
   120  	return nil
   121  }