github.com/nya3jp/tast@v0.0.0-20230601000426-85c8e4d83a9b/src/go.chromium.org/tast/core/cmd/tast-lint/internal/check/issue.go (about)

     1  // Copyright 2018 The ChromiumOS Authors
     2  // Use of this source code is governed by a BSD-style license that can be
     3  // found in the LICENSE file.
     4  
     5  package check
     6  
     7  import (
     8  	"fmt"
     9  	"go/ast"
    10  	"go/token"
    11  	"sort"
    12  	"strings"
    13  )
    14  
    15  // Issue holds an issue reported by the linter.
    16  type Issue struct {
    17  	Pos     token.Position
    18  	Msg     string
    19  	Link    string
    20  	Fixable bool
    21  	Warning bool
    22  }
    23  
    24  func (i *Issue) String() string {
    25  	return fmt.Sprintf("%s: %s", i.Pos, i.Msg)
    26  }
    27  
    28  // SortIssues sorts issues by file path and position.
    29  func SortIssues(issues []*Issue) {
    30  	sort.Slice(issues, func(i, j int) bool {
    31  		pi := issues[i].Pos
    32  		pj := issues[j].Pos
    33  		if pi.Filename != pj.Filename {
    34  			return pi.Filename < pj.Filename
    35  		}
    36  		return pi.Offset < pj.Offset
    37  	})
    38  }
    39  
    40  // DropIgnoredIssues drops all issues that are on the same lines as NOLINT comments.
    41  //
    42  // Specifically, an issue is dropped if its line number matches the starting line
    43  // number of a comment group (see ast.CommentGroup) in f that contains "NOLINT".
    44  //
    45  // Filtering is performed at this level rather than while walking the AST for several reasons:
    46  //   - We want to avoid making each check look for NOLINT itself.
    47  //   - We can't just skip nodes that are on the same lines as NOLINT comments, since some issues are
    48  //     reported with different line numbers than the position of the node from which they were reported.
    49  //     For example, the Declarations function inspects testing.Test composite literal nodes,
    50  //     but the line numbers used in its issues correspond to Desc fields that contain errors.
    51  //     We expect test authors to place a NOLINT comment at the end of the line containing the Desc field,
    52  //     not on the line containing the beginning of the testing.Test literal.
    53  func DropIgnoredIssues(issues []*Issue, fs *token.FileSet, f *ast.File) []*Issue {
    54  	nolintLineNums := make(map[int]struct{})
    55  	for _, cg := range f.Comments {
    56  		if strings.Contains(cg.Text(), "NOLINT") {
    57  			lineNum := fs.File(cg.Pos()).Line(cg.Pos())
    58  			nolintLineNums[lineNum] = struct{}{}
    59  		}
    60  	}
    61  
    62  	var kept []*Issue
    63  	for _, issue := range issues {
    64  		if _, ok := nolintLineNums[issue.Pos.Line]; !ok {
    65  			kept = append(kept, issue)
    66  		}
    67  	}
    68  	return kept
    69  }