Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions go/analysis/passes/gofmt/doc.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
// Copyright 2026 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 gofmt defines an Analyzer that reports lines that would be
// changed by gofmt.
//
// # Analyzer gofmt
//
// gofmt: report lines whose formatting differs from gofmt's
//
// This analyzer reports diagnostic warnings for lines in Go source files
// that are not formatted according to gofmt. Each diagnostic includes a
// suggested fix to apply the correct formatting.
package gofmt
76 changes: 76 additions & 0 deletions go/analysis/passes/gofmt/gofmt.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
// Copyright 2026 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 gofmt

import (
"bytes"
_ "embed"
"go/ast"
"go/format"

"golang.org/x/tools/go/analysis"
"golang.org/x/tools/internal/analysis/analyzerutil"
"golang.org/x/tools/internal/diff"
"golang.org/x/tools/internal/goplsexport"
)

//go:embed doc.go
var doc string

var analyzer = &analysis.Analyzer{
Name: "gofmt",
Doc: analyzerutil.MustExtractDoc(doc, "gofmt"),
URL: "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/gofmt",
Run: run,
RunDespiteErrors: true,
}

func init() {
// Export to gopls until this is a published analyzer.
goplsexport.GofmtAnalyzer = analyzer
}

func run(pass *analysis.Pass) (any, error) {
for _, file := range pass.Files {
if ast.IsGenerated(file) {
continue
}
tokenFile := pass.Fset.File(file.FileStart)
filename := tokenFile.Name()

src, err := pass.ReadFile(filename)
if err != nil {
continue
}

formatted, err := format.Source(src)
if err != nil {
continue
}

if bytes.Equal(src, formatted) {
continue
}

for _, edit := range diff.Lines(string(src), string(formatted)) {
pos := tokenFile.Pos(edit.Start)
end := tokenFile.Pos(edit.End)
pass.Report(analysis.Diagnostic{
Pos: pos,
End: end,
Message: "file not formatted correctly",
SuggestedFixes: []analysis.SuggestedFix{{
Message: "Format with gofmt",
TextEdits: []analysis.TextEdit{{
Pos: pos,
End: end,
NewText: []byte(edit.New),
}},
}},
})
}
}
return nil, nil
}
19 changes: 19 additions & 0 deletions go/analysis/passes/gofmt/gofmt_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
// Copyright 2026 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 gofmt_test

import (
"testing"

"golang.org/x/tools/go/analysis/analysistest"
"golang.org/x/tools/internal/goplsexport"

_ "golang.org/x/tools/go/analysis/passes/gofmt"
)

func Test(t *testing.T) {
testdata := analysistest.TestData()
analysistest.RunWithSuggestedFixes(t, testdata, goplsexport.GofmtAnalyzer, "a")
}
9 changes: 9 additions & 0 deletions go/analysis/passes/gofmt/testdata/src/a/a.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package a

import "fmt" // want "file not formatted correctly"

func f( ) { // want "file not formatted correctly"

x:=1 // want "file not formatted correctly"
fmt.Println(x)
}
9 changes: 9 additions & 0 deletions go/analysis/passes/gofmt/testdata/src/a/a.go.golden
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package a

import "fmt" // want "file not formatted correctly"

func f() { // want "file not formatted correctly"

x := 1 // want "file not formatted correctly"
fmt.Println(x)
}
10 changes: 10 additions & 0 deletions gopls/doc/analyzers.md
Original file line number Diff line number Diff line change
Expand Up @@ -3227,6 +3227,16 @@ Default: on.

Package documentation: [framepointer](https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/framepointer)

<a id='gofmt'></a>
## `gofmt`: report lines whose formatting differs from gofmt's

This analyzer reports diagnostic warnings for lines in Go source files that are not formatted according to gofmt. Each diagnostic includes a suggested fix to apply the correct formatting.


Default: off. Enable by setting `"analyses": {"gofmt": true}`.

Package documentation: [gofmt](https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/gofmt)

<a id='hostport'></a>
## `hostport`: check format of addresses passed to net.Dial

Expand Down
12 changes: 12 additions & 0 deletions gopls/internal/doc/api.json
Original file line number Diff line number Diff line change
Expand Up @@ -1496,6 +1496,12 @@
"Default": "true",
"Status": ""
},
{
"Name": "\"gofmt\"",
"Doc": "report lines whose formatting differs from gofmt's\n\nThis analyzer reports diagnostic warnings for lines in Go source files\nthat are not formatted according to gofmt. Each diagnostic includes a\nsuggested fix to apply the correct formatting.",
"Default": "false",
"Status": ""
},
{
"Name": "\"hostport\"",
"Doc": "check format of addresses passed to net.Dial\n\nThis analyzer flags code that produce network address strings using\nfmt.Sprintf, as in this example:\n\n addr := fmt.Sprintf(\"%s:%d\", host, 12345) // \"will not work with IPv6\"\n ...\n conn, err := net.Dial(\"tcp\", addr) // \"when passed to dial here\"\n\nThe analyzer suggests a fix to use the correct approach, a call to\nnet.JoinHostPort:\n\n addr := net.JoinHostPort(host, \"12345\")\n ...\n conn, err := net.Dial(\"tcp\", addr)\n\nA similar diagnostic and fix are produced for a format string of \"%s:%s\".\n",
Expand Down Expand Up @@ -3443,6 +3449,12 @@
"URL": "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/framepointer",
"Default": true
},
{
"Name": "gofmt",
"Doc": "report lines whose formatting differs from gofmt's\n\nThis analyzer reports diagnostic warnings for lines in Go source files\nthat are not formatted according to gofmt. Each diagnostic includes a\nsuggested fix to apply the correct formatting.",
"URL": "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/gofmt",
"Default": false
},
{
"Name": "hostport",
"Doc": "check format of addresses passed to net.Dial\n\nThis analyzer flags code that produce network address strings using\nfmt.Sprintf, as in this example:\n\n addr := fmt.Sprintf(\"%s:%d\", host, 12345) // \"will not work with IPv6\"\n ...\n conn, err := net.Dial(\"tcp\", addr) // \"when passed to dial here\"\n\nThe analyzer suggests a fix to use the correct approach, a call to\nnet.JoinHostPort:\n\n addr := net.JoinHostPort(host, \"12345\")\n ...\n conn, err := net.Dial(\"tcp\", addr)\n\nA similar diagnostic and fix are produced for a format string of \"%s:%s\".\n",
Expand Down
4 changes: 4 additions & 0 deletions gopls/internal/settings/analysis.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import (
"golang.org/x/tools/go/analysis/passes/errorsas"
"golang.org/x/tools/go/analysis/passes/fieldalignment"
"golang.org/x/tools/go/analysis/passes/framepointer"
_ "golang.org/x/tools/go/analysis/passes/gofmt"
"golang.org/x/tools/go/analysis/passes/hostport"
"golang.org/x/tools/go/analysis/passes/httpresponse"
"golang.org/x/tools/go/analysis/passes/ifaceassert"
Expand Down Expand Up @@ -220,6 +221,9 @@ var DefaultAnalyzers = []*Analyzer{
{analyzer: recursiveiter.Analyzer}, // under evaluation
{analyzer: writestring.Analyzer},

// disabled by default
{analyzer: goplsexport.GofmtAnalyzer, severity: protocol.SeverityInformation, nonDefault: true},

// disabled due to high false positives
{analyzer: shadow.Analyzer, severity: protocol.SeverityHint, nonDefault: true}, // very noisy
{analyzer: fieldalignment.Analyzer, severity: protocol.SeverityHint, nonDefault: true}, // #67762, #76237
Expand Down
1 change: 1 addition & 0 deletions internal/goplsexport/export.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,5 @@ var (
UnsafeFuncsModernizer *analysis.Analyzer // = modernize.unsafeFuncsAnalyzer
AtomicTypesModernizer *analysis.Analyzer // = modernize.atomicTypesAnalyzer
EmbedLitModernizer *analysis.Analyzer // = modernize.embedLitAnalyzer
GofmtAnalyzer *analysis.Analyzer // = gofmt.analyzer
)