GitHub

@@ -0,0 +1,107 @@

1+

package facts

2+3+

import (

4+

"go/ast"

5+

"go/token"

6+

"path/filepath"

7+

"reflect"

8+

"strings"

9+10+

"golang.org/x/tools/go/analysis"

11+

)

12+13+

// A directive is a comment of the form '//lint:<command>

14+

// [arguments...]'. It represents instructions to the static analysis

15+

// tool.

16+

type Directive struct {

17+

Command string

18+

Arguments []string

19+

Directive *ast.Comment

20+

Node ast.Node

21+

}

22+23+

type SerializedDirective struct {

24+

Command string

25+

Arguments []string

26+

// The position of the comment

27+

DirectivePosition token.Position

28+

// The position of the node that the comment is attached to

29+

NodePosition token.Position

30+

}

31+32+

func parseDirective(s string) (cmd string, args []string) {

33+

if !strings.HasPrefix(s, "//lint:") {

34+

return "", nil

35+

}

36+

s = strings.TrimPrefix(s, "//lint:")

37+

fields := strings.Split(s, " ")

38+

return fields[0], fields[1:]

39+

}

40+41+

func directives(pass *analysis.Pass) (interface{}, error) {

42+

return ParseDirectives(pass.Files, pass.Fset), nil

43+

}

44+45+

func ParseDirectives(files []*ast.File, fset *token.FileSet) []Directive {

46+

var dirs []Directive

47+

for _, f := range files {

48+

// OPT(dh): in our old code, we skip all the commentmap work if we

49+

// couldn't find any directives, benchmark if that's actually

50+

// worth doing

51+

cm := ast.NewCommentMap(fset, f, f.Comments)

52+

for node, cgs := range cm {

53+

for _, cg := range cgs {

54+

for _, c := range cg.List {

55+

if !strings.HasPrefix(c.Text, "//lint:") {

56+

continue

57+

}

58+

cmd, args := parseDirective(c.Text)

59+

d := Directive{

60+

Command: cmd,

61+

Arguments: args,

62+

Directive: c,

63+

Node: node,

64+

}

65+

dirs = append(dirs, d)

66+

}

67+

}

68+

}

69+

}

70+

return dirs

71+

}

72+73+

// duplicated from report.DisplayPosition to break import cycle

74+

func displayPosition(fset *token.FileSet, p token.Pos) token.Position {

75+

if p == token.NoPos {

76+

return token.Position{}

77+

}

78+79+

// Only use the adjusted position if it points to another Go file.

80+

// This means we'll point to the original file for cgo files, but

81+

// we won't point to a YACC grammar file.

82+

pos := fset.PositionFor(p, false)

83+

adjPos := fset.PositionFor(p, true)

84+85+

if filepath.Ext(adjPos.Filename) == ".go" {

86+

return adjPos

87+

}

88+89+

return pos

90+

}

91+92+

var Directives = &analysis.Analyzer{

93+

Name: "directives",

94+

Doc: "extracts linter directives",

95+

Run: directives,

96+

RunDespiteErrors: true,

97+

ResultType: reflect.TypeOf([]Directive{}),

98+

}

99+100+

func SerializeDirective(dir Directive, fset *token.FileSet) SerializedDirective {

101+

return SerializedDirective{

102+

Command: dir.Command,

103+

Arguments: dir.Arguments,

104+

DirectivePosition: displayPosition(fset, dir.Directive.Pos()),

105+

NodePosition: displayPosition(fset, dir.Node.Pos()),

106+

}

107+

}

Read the original on github.com ↗