Compare commits
3 Commits
master
...
release-br
Author | SHA1 | Date |
---|---|---|
|
aa82965741 | |
|
86c5873b48 | |
|
49d818b077 |
|
@ -134,6 +134,46 @@ func killAndWait(cmd *exec.Cmd) {
|
||||||
cmd.Wait()
|
cmd.Wait()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestURL(t *testing.T) {
|
||||||
|
if runtime.GOOS == "plan9" {
|
||||||
|
t.Skip("skipping on plan9; fails to start up quickly enough")
|
||||||
|
}
|
||||||
|
bin, cleanup := buildGodoc(t)
|
||||||
|
defer cleanup()
|
||||||
|
|
||||||
|
testcase := func(url string, contents string) func(t *testing.T) {
|
||||||
|
return func(t *testing.T) {
|
||||||
|
stdout, stderr := new(bytes.Buffer), new(bytes.Buffer)
|
||||||
|
|
||||||
|
args := []string{fmt.Sprintf("-url=%s", url)}
|
||||||
|
cmd := exec.Command(bin, args...)
|
||||||
|
cmd.Stdout = stdout
|
||||||
|
cmd.Stderr = stderr
|
||||||
|
cmd.Args[0] = "godoc"
|
||||||
|
|
||||||
|
// Set GOPATH variable to non-existing path
|
||||||
|
// and GOPROXY=off to disable module fetches.
|
||||||
|
// We cannot just unset GOPATH variable because godoc would default it to ~/go.
|
||||||
|
// (We don't want the indexer looking at the local workspace during tests.)
|
||||||
|
cmd.Env = append(os.Environ(),
|
||||||
|
"GOPATH=does_not_exist",
|
||||||
|
"GOPROXY=off",
|
||||||
|
"GO111MODULE=off")
|
||||||
|
|
||||||
|
if err := cmd.Run(); err != nil {
|
||||||
|
t.Fatalf("failed to run godoc -url=%q: %s\nstderr:\n%s", url, err, stderr)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !strings.Contains(stdout.String(), contents) {
|
||||||
|
t.Errorf("did not find substring %q in output of godoc -url=%q:\n%s", contents, url, stdout)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
t.Run("index", testcase("/", "Go is an open source programming language"))
|
||||||
|
t.Run("fmt", testcase("/pkg/fmt", "Package fmt implements formatted I/O"))
|
||||||
|
}
|
||||||
|
|
||||||
// Basic integration test for godoc HTTP interface.
|
// Basic integration test for godoc HTTP interface.
|
||||||
func TestWeb(t *testing.T) {
|
func TestWeb(t *testing.T) {
|
||||||
testWeb(t, false)
|
testWeb(t, false)
|
||||||
|
@ -150,7 +190,7 @@ func TestWebIndex(t *testing.T) {
|
||||||
// Basic integration test for godoc HTTP interface.
|
// Basic integration test for godoc HTTP interface.
|
||||||
func testWeb(t *testing.T, withIndex bool) {
|
func testWeb(t *testing.T, withIndex bool) {
|
||||||
if runtime.GOOS == "plan9" {
|
if runtime.GOOS == "plan9" {
|
||||||
t.Skip("skipping on plan9; files to start up quickly enough")
|
t.Skip("skipping on plan9; fails to start up quickly enough")
|
||||||
}
|
}
|
||||||
bin, cleanup := buildGodoc(t)
|
bin, cleanup := buildGodoc(t)
|
||||||
defer cleanup()
|
defer cleanup()
|
||||||
|
|
|
@ -94,7 +94,7 @@ type httpResponseRecorder struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *httpResponseRecorder) Header() http.Header { return w.header }
|
func (w *httpResponseRecorder) Header() http.Header { return w.header }
|
||||||
func (w *httpResponseRecorder) Write(b []byte) (int, error) { return len(b), nil }
|
func (w *httpResponseRecorder) Write(b []byte) (int, error) { return w.body.Write(b) }
|
||||||
func (w *httpResponseRecorder) WriteHeader(code int) { w.code = code }
|
func (w *httpResponseRecorder) WriteHeader(code int) { w.code = code }
|
||||||
|
|
||||||
func usage() {
|
func usage() {
|
||||||
|
@ -168,8 +168,8 @@ func main() {
|
||||||
fmt.Fprintln(os.Stderr, `Unexpected arguments. Use "go doc" for command-line help output instead. For example, "go doc fmt.Printf".`)
|
fmt.Fprintln(os.Stderr, `Unexpected arguments. Use "go doc" for command-line help output instead. For example, "go doc fmt.Printf".`)
|
||||||
usage()
|
usage()
|
||||||
}
|
}
|
||||||
if *httpAddr != "" && *urlFlag != "" && !*writeIndex {
|
if *httpAddr == "" && *urlFlag == "" && !*writeIndex {
|
||||||
fmt.Fprintln(os.Stderr, "Missing args.")
|
fmt.Fprintln(os.Stderr, "At least one of -http, -url, or -write_index must be set to a non-zero value.")
|
||||||
usage()
|
usage()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -228,7 +228,7 @@ func main() {
|
||||||
corpus.IndexDirectory = indexDirectoryDefault
|
corpus.IndexDirectory = indexDirectoryDefault
|
||||||
corpus.IndexThrottle = *indexThrottle
|
corpus.IndexThrottle = *indexThrottle
|
||||||
corpus.IndexInterval = *indexInterval
|
corpus.IndexInterval = *indexInterval
|
||||||
if *writeIndex {
|
if *writeIndex || *urlFlag != "" {
|
||||||
corpus.IndexThrottle = 1.0
|
corpus.IndexThrottle = 1.0
|
||||||
corpus.IndexEnabled = true
|
corpus.IndexEnabled = true
|
||||||
initCorpus(corpus)
|
initCorpus(corpus)
|
||||||
|
|
|
@ -453,15 +453,23 @@ func printfNameAndKind(pass *analysis.Pass, call *ast.CallExpr) (fn *types.Func,
|
||||||
}
|
}
|
||||||
|
|
||||||
// isFormatter reports whether t satisfies fmt.Formatter.
|
// isFormatter reports whether t satisfies fmt.Formatter.
|
||||||
// Unlike fmt.Stringer, it's impossible to satisfy fmt.Formatter without importing fmt.
|
// The only interface method to look for is "Format(State, rune)".
|
||||||
func isFormatter(pass *analysis.Pass, t types.Type) bool {
|
func isFormatter(typ types.Type) bool {
|
||||||
for _, imp := range pass.Pkg.Imports() {
|
obj, _, _ := types.LookupFieldOrMethod(typ, false, nil, "Format")
|
||||||
if imp.Path() == "fmt" {
|
fn, ok := obj.(*types.Func)
|
||||||
formatter := imp.Scope().Lookup("Formatter").Type().Underlying().(*types.Interface)
|
if !ok {
|
||||||
return types.Implements(t, formatter)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
return false
|
||||||
|
}
|
||||||
|
sig := fn.Type().(*types.Signature)
|
||||||
|
return sig.Params().Len() == 2 &&
|
||||||
|
sig.Results().Len() == 0 &&
|
||||||
|
isNamed(sig.Params().At(0).Type(), "fmt", "State") &&
|
||||||
|
types.Identical(sig.Params().At(1).Type(), types.Typ[types.Rune])
|
||||||
|
}
|
||||||
|
|
||||||
|
func isNamed(T types.Type, pkgpath, name string) bool {
|
||||||
|
named, ok := T.(*types.Named)
|
||||||
|
return ok && named.Obj().Pkg().Path() == pkgpath && named.Obj().Name() == name
|
||||||
}
|
}
|
||||||
|
|
||||||
// formatState holds the parsed representation of a printf directive such as "%3.*[4]d".
|
// formatState holds the parsed representation of a printf directive such as "%3.*[4]d".
|
||||||
|
@ -753,7 +761,7 @@ func okPrintfArg(pass *analysis.Pass, call *ast.CallExpr, state *formatState) (o
|
||||||
formatter := false
|
formatter := false
|
||||||
if state.argNum < len(call.Args) {
|
if state.argNum < len(call.Args) {
|
||||||
if tv, ok := pass.TypesInfo.Types[call.Args[state.argNum]]; ok {
|
if tv, ok := pass.TypesInfo.Types[call.Args[state.argNum]]; ok {
|
||||||
formatter = isFormatter(pass, tv.Type)
|
formatter = isFormatter(tv.Type)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -831,7 +839,7 @@ func recursiveStringer(pass *analysis.Pass, e ast.Expr) bool {
|
||||||
typ := pass.TypesInfo.Types[e].Type
|
typ := pass.TypesInfo.Types[e].Type
|
||||||
|
|
||||||
// It's unlikely to be a recursive stringer if it has a Format method.
|
// It's unlikely to be a recursive stringer if it has a Format method.
|
||||||
if isFormatter(pass, typ) {
|
if isFormatter(typ) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -10,5 +10,5 @@ import (
|
||||||
func Test(t *testing.T) {
|
func Test(t *testing.T) {
|
||||||
testdata := analysistest.TestData()
|
testdata := analysistest.TestData()
|
||||||
printf.Analyzer.Flags.Set("funcs", "Warn,Warnf")
|
printf.Analyzer.Flags.Set("funcs", "Warn,Warnf")
|
||||||
analysistest.Run(t, testdata, printf.Analyzer, "a", "b")
|
analysistest.Run(t, testdata, printf.Analyzer, "a", "b", "nofmt")
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,10 @@
|
||||||
|
package b
|
||||||
|
|
||||||
|
import (
|
||||||
|
"math/big"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func formatBigInt(t *testing.T) {
|
||||||
|
t.Logf("%d\n", big.NewInt(4))
|
||||||
|
}
|
|
@ -38,7 +38,7 @@ func matchArgTypeInternal(pass *analysis.Pass, t printfArgType, typ types.Type,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// If the type implements fmt.Formatter, we have nothing to check.
|
// If the type implements fmt.Formatter, we have nothing to check.
|
||||||
if isFormatter(pass, typ) {
|
if isFormatter(typ) {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
// If we can use a string, might arg (dynamically) implement the Stringer or Error interface?
|
// If we can use a string, might arg (dynamically) implement the Stringer or Error interface?
|
||||||
|
|
|
@ -96,6 +96,11 @@ func checkTagDuplicates(pass *analysis.Pass, tag, key string, nearest, field *ty
|
||||||
}
|
}
|
||||||
if val == "" || val[0] == ',' {
|
if val == "" || val[0] == ',' {
|
||||||
if field.Anonymous() {
|
if field.Anonymous() {
|
||||||
|
// Disable this check enhancement in Go 1.12.1; some
|
||||||
|
// false positives were spotted in the initial 1.12
|
||||||
|
// release. See https://golang.org/issues/30465.
|
||||||
|
return
|
||||||
|
|
||||||
typ, ok := field.Type().Underlying().(*types.Struct)
|
typ, ok := field.Type().Underlying().(*types.Struct)
|
||||||
if !ok {
|
if !ok {
|
||||||
return
|
return
|
||||||
|
|
|
@ -75,7 +75,7 @@ type DuplicateJSONFields struct {
|
||||||
}
|
}
|
||||||
AnonymousJSON `json:"a"` // want "struct field AnonymousJSON repeats json tag .a. also at a.go:64"
|
AnonymousJSON `json:"a"` // want "struct field AnonymousJSON repeats json tag .a. also at a.go:64"
|
||||||
|
|
||||||
AnonymousJSONField // want "struct field DuplicateAnonJSON repeats json tag .a. also at a.go:64"
|
AnonymousJSONField
|
||||||
|
|
||||||
XML int `xml:"a"`
|
XML int `xml:"a"`
|
||||||
DuplicateXML int `xml:"a"` // want "struct field DuplicateXML repeats xml tag .a. also at a.go:80"
|
DuplicateXML int `xml:"a"` // want "struct field DuplicateXML repeats xml tag .a. also at a.go:80"
|
||||||
|
@ -129,5 +129,5 @@ type DuplicateWithAnotherPackage struct {
|
||||||
|
|
||||||
// The "also at" position is in a different package and directory. Use
|
// The "also at" position is in a different package and directory. Use
|
||||||
// "b.b" instead of "b/b" to match the relative path on Windows easily.
|
// "b.b" instead of "b/b" to match the relative path on Windows easily.
|
||||||
DuplicateJSON int `json:"a"` // want "struct field DuplicateJSON repeats json tag .a. also at b.b.go:8"
|
DuplicateJSON int `json:"a"`
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue