Welcome to mirror list, hosted at ThFree Co, Russian Federation.

quote.go « gitaly « golangci-lint « tools - gitlab.com/gitlab-org/gitaly.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
blob: cfc2dac9780c12bcb5eac834023c932b4651b29b (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
package main

import (
	"go/ast"
	"go/token"
	"regexp"
	"strings"

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

const quoteInterpolationAnalyzerName = "string_interpolation_quote"

type quoteInterpolationAnalyzerSettings struct {
	IncludedFunctions []string `mapstructure:"included-functions"`
}

// newQuoteInterpolationAnalyzer returns an analyzer to detect manually quoted string interpolation
// with '%s' and "%s". Quoting this way doesn't escape special characters such as endline and makes
// debugging harder later. We encourage to use %q verb instead.
//
//   - Bad
//     return fmt.Errorf("gl_id='%s' is invalid", glID)
//
//   - Bad
//     fmt.Sprintf("fatal: not a git repository: '%s'", repoPath)
//
//   - Good
//     return fmt.Errorf("gl_id=%q is invalid", glID)
//
//   - Good
//     fmt.Sprintf("fatal: not a git repository: %q", repoPath)
//
// For more information:
// https://gitlab.com/gitlab-org/gitaly/-/blob/master/STYLE.md#use-q-when-interpolating-strings
func newQuoteInterpolationAnalyzer(settings *quoteInterpolationAnalyzerSettings) *analysis.Analyzer {
	return &analysis.Analyzer{
		Name: quoteInterpolationAnalyzerName,
		Doc: `Unless it would lead to incorrect results, always use %q when
		interpolating strings. For more information:
		https://gitlab.com/gitlab-org/gitaly/-/blob/master/STYLE.md#use-q-when-interpolating-strings`,
		Run: runStringInterpolationQuoteAnalyzer(settings.IncludedFunctions),
	}
}

// offendedFormatPattern matches string interpolation having '%s' and "%s" format
var offendedFormatPattern = regexp.MustCompile(`['"]%s['"]`)

func analyzeInterpolation(call *ast.CallExpr, pass *analysis.Pass) {
	if len(call.Args) <= 1 {
		return
	}

	if str, ok := call.Args[0].(*ast.BasicLit); ok && str.Kind == token.STRING {
		value := str.Value
		if strings.HasPrefix(value, `'`) || strings.HasPrefix(value, `"`) {
			value = value[1:]
		}
		if strings.HasSuffix(value, `'`) || strings.HasSuffix(value, `"`) {
			value = value[:len(value)-1]
		}
		for _, index := range offendedFormatPattern.FindAllIndex([]byte(value), -1) {
			start := token.Pos(int(str.Pos()) + index[0] + 1)
			end := token.Pos(int(str.Pos()) + index[1])
			pass.Report(analysis.Diagnostic{
				Pos:            start,
				End:            end,
				Message:        "wrapping %s verb with quotes is not encouraged, please use %q instead",
				SuggestedFixes: nil,
			})
		}
	}
}

func runStringInterpolationQuoteAnalyzer(rules []string) func(*analysis.Pass) (interface{}, error) {
	return func(pass *analysis.Pass) (interface{}, error) {
		matcher := NewMatcher(pass)
		for _, file := range pass.Files {
			ast.Inspect(file, func(n ast.Node) bool {
				if call, ok := n.(*ast.CallExpr); ok {
					if matcher.MatchFunction(call, rules) {
						analyzeInterpolation(call, pass)
					}
				}
				return true
			})
		}
		return nil, nil
	}
}