aboutsummaryrefslogtreecommitdiff
path: root/gopls/internal/regtest/misc/staticcheck_test.go
blob: fa049ab0e5f1629e520b68455d4652a9919fcddc (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
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
// Copyright 2022 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package misc

import (
	"testing"

	"golang.org/x/tools/internal/testenv"

	. "golang.org/x/tools/gopls/internal/lsp/regtest"
)

func TestStaticcheckGenerics(t *testing.T) {
	testenv.NeedsGo1Point(t, 19) // generics were introduced in Go 1.18, staticcheck requires go1.19+

	const files = `
-- go.mod --
module mod.com

go 1.18
-- a/a.go --
package a

import (
	"errors"
	"sort"
	"strings"
)

func Zero[P any]() P {
	var p P
	return p
}

type Inst[P any] struct {
	Field P
}

func testGenerics[P *T, T any](p P) {
	// Calls to instantiated functions should not break checks.
	slice := Zero[string]()
	sort.Slice(slice, func(i, j int) bool {
		return slice[i] < slice[j]
	})

	// Usage of instantiated fields should not break checks.
	g := Inst[string]{"hello"}
	g.Field = strings.TrimLeft(g.Field, "12234")

	// Use of type parameters should not break checks.
	var q P
	p = q // SA4009: p is overwritten before its first use
	q = &*p // SA4001: &* will be simplified
}


// FooErr should be called ErrFoo (ST1012)
var FooErr error = errors.New("foo")
`

	WithOptions(
		Settings{"staticcheck": true},
	).Run(t, files, func(t *testing.T, env *Env) {
		env.OpenFile("a/a.go")
		env.AfterChange(
			Diagnostics(env.AtRegexp("a/a.go", "sort.Slice"), FromSource("sortslice")),
			Diagnostics(env.AtRegexp("a/a.go", "sort.Slice.(slice)"), FromSource("SA1028")),
			Diagnostics(env.AtRegexp("a/a.go", "var (FooErr)"), FromSource("ST1012")),
			Diagnostics(env.AtRegexp("a/a.go", `"12234"`), FromSource("SA1024")),
			Diagnostics(env.AtRegexp("a/a.go", "testGenerics.*(p P)"), FromSource("SA4009")),
			Diagnostics(env.AtRegexp("a/a.go", "q = (&\\*p)"), FromSource("SA4001")),
		)
	})
}

// Test for golang/go#56270: an analysis with related info should not panic if
// analysis.RelatedInformation.End is not set.
func TestStaticcheckRelatedInfo(t *testing.T) {
	testenv.NeedsGo1Point(t, 19) // staticcheck is only supported at Go 1.19+
	const files = `
-- go.mod --
module mod.test

go 1.18
-- p.go --
package p

import (
	"fmt"
)

func Foo(enabled interface{}) {
	if enabled, ok := enabled.(bool); ok {
	} else {
		_ = fmt.Sprintf("invalid type %T", enabled) // enabled is always bool here
	}
}
`

	WithOptions(
		Settings{"staticcheck": true},
	).Run(t, files, func(t *testing.T, env *Env) {
		env.OpenFile("p.go")
		env.AfterChange(
			Diagnostics(env.AtRegexp("p.go", ", (enabled)"), FromSource("SA9008")),
		)
	})
}