blob: 6d31e5796c6525e97da1ebade0278ef7b5e4e5e4 [file] [log] [blame]
// Copyright 2024 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 (
"runtime"
"testing"
"golang.org/x/tools/gopls/internal/protocol"
"golang.org/x/tools/gopls/internal/server"
"golang.org/x/tools/gopls/internal/settings"
. "golang.org/x/tools/gopls/internal/test/integration"
)
// TestCompilerOptDetails exercises the "{Show,Hide} compiler optimization details" code action.
func TestCompilerOptDetails(t *testing.T) {
if runtime.GOOS == "android" {
t.Skipf("the compiler optimization details code action doesn't work on Android")
}
const mod = `
-- go.mod --
module mod.com
go 1.18
-- main.go --
package main
import "fmt"
func main() {
fmt.Println(42)
}
`
Run(t, mod, func(t *testing.T, env *Env) {
env.OpenFile("main.go")
actions := env.CodeActionForFile("main.go", nil)
// Execute the "Show compiler optimization details" command.
docAction, err := CodeActionByKind(actions, settings.GoToggleCompilerOptDetails)
if err != nil {
t.Fatal(err)
}
params := &protocol.ExecuteCommandParams{
Command: docAction.Command.Command,
Arguments: docAction.Command.Arguments,
}
env.ExecuteCommand(params, nil)
env.OnceMet(
CompletedWork(server.DiagnosticWorkTitle(server.FromToggleCompilerOptDetails), 1, true),
Diagnostics(
ForFile("main.go"),
AtPosition("main.go", 5, 13), // (LSP coordinates)
WithMessage("42 escapes"),
WithSeverityTags("optimizer details", protocol.SeverityInformation, nil),
),
)
// Diagnostics should be reported even on unsaved
// edited buffers, thanks to the magic of overlays.
env.SetBufferContent("main.go", `
package main
func main() { _ = f }
func f(x int) *int { return &x }`)
env.AfterChange(Diagnostics(
ForFile("main.go"),
WithMessage("x escapes"),
WithSeverityTags("optimizer details", protocol.SeverityInformation, nil),
))
// Toggle the flag again so now it should be off.
env.ExecuteCommand(params, nil)
env.OnceMet(
CompletedWork(server.DiagnosticWorkTitle(server.FromToggleCompilerOptDetails), 2, true),
NoDiagnostics(ForFile("main.go")),
)
})
}
// TestCompilerOptDetails_perDirectory exercises that the "want
// optimization details" flag has per-directory cardinality.
func TestCompilerOptDetails_perDirectory(t *testing.T) {
if runtime.GOOS == "android" {
t.Skipf("the compiler optimization details code action doesn't work on Android")
}
const mod = `
-- go.mod --
module mod.com
go 1.18
-- a/a.go --
package a
func F(x int) any { return &x }
-- a/a_test.go --
package a
func G(x int) any { return &x }
-- a/a_x_test.go --
package a_test
func H(x int) any { return &x }
`
Run(t, mod, func(t *testing.T, env *Env) {
// toggle executes the "Toggle compiler optimization details"
// command within a file, and asserts that it has the specified title.
toggle := func(filename, wantTitle string) {
env.OpenFile(filename)
actions := env.CodeActionForFile(filename, nil)
docAction, err := CodeActionByKind(actions, settings.GoToggleCompilerOptDetails)
if err != nil {
t.Fatal(err)
}
if docAction.Title != wantTitle {
t.Errorf("CodeAction.Title = %q, want %q", docAction.Title, wantTitle)
}
params := &protocol.ExecuteCommandParams{
Command: docAction.Command.Command,
Arguments: docAction.Command.Arguments,
}
env.ExecuteCommand(params, nil)
}
// Show diagnostics for directory a/ from one file.
// Diagnostics are reported for all three packages.
toggle("a/a.go", `Show compiler optimization details for "a"`)
env.OnceMet(
CompletedWork(server.DiagnosticWorkTitle(server.FromToggleCompilerOptDetails), 1, true),
Diagnostics(
ForFile("a/a.go"),
AtPosition("a/a.go", 2, 7),
WithMessage("x escapes to heap"),
WithSeverityTags("optimizer details", protocol.SeverityInformation, nil),
),
Diagnostics(
ForFile("a/a_test.go"),
AtPosition("a/a_test.go", 2, 7),
WithMessage("x escapes to heap"),
WithSeverityTags("optimizer details", protocol.SeverityInformation, nil),
),
Diagnostics(
ForFile("a/a_x_test.go"),
AtPosition("a/a_x_test.go", 2, 7),
WithMessage("x escapes to heap"),
WithSeverityTags("optimizer details", protocol.SeverityInformation, nil),
),
)
// Hide diagnostics for the directory from a different file.
// All diagnostics disappear.
toggle("a/a_test.go", `Hide compiler optimization details for "a"`)
env.OnceMet(
CompletedWork(server.DiagnosticWorkTitle(server.FromToggleCompilerOptDetails), 2, true),
NoDiagnostics(ForFile("a/a.go")),
NoDiagnostics(ForFile("a/a_test.go")),
NoDiagnostics(ForFile("a/a_x_test.go")),
)
})
}
// TestCompilerOptDetails_config exercises that the "want optimization
// details" flag honors the "annotation" configuration setting.
func TestCompilerOptDetails_config(t *testing.T) {
if runtime.GOOS == "android" {
t.Skipf("the compiler optimization details code action doesn't work on Android")
}
const mod = `
-- go.mod --
module mod.com
go 1.18
-- a/a.go --
package a
func F(x int) any { return &x } // escape(x escapes to heap)
func G() { defer func(){} () } // cannotInlineFunction(unhandled op DEFER)
`
for _, escape := range []bool{true, false} {
WithOptions(
Settings{"annotations": map[string]any{"inline": true, "escape": escape}},
).Run(t, mod, func(t *testing.T, env *Env) {
env.OpenFile("a/a.go")
actions := env.CodeActionForFile("a/a.go", nil)
docAction, err := CodeActionByKind(actions, settings.GoToggleCompilerOptDetails)
if err != nil {
t.Fatal(err)
}
params := &protocol.ExecuteCommandParams{
Command: docAction.Command.Command,
Arguments: docAction.Command.Arguments,
}
env.ExecuteCommand(params, nil)
env.OnceMet(
CompletedWork(server.DiagnosticWorkTitle(server.FromToggleCompilerOptDetails), 1, true),
cond(escape, Diagnostics, NoDiagnostics)(
ForFile("a/a.go"),
AtPosition("a/a.go", 2, 7),
WithMessage("x escapes to heap"),
WithSeverityTags("optimizer details", protocol.SeverityInformation, nil),
),
Diagnostics(
ForFile("a/a.go"),
AtPosition("a/a.go", 3, 5),
WithMessage("cannotInlineFunction(unhandled op DEFER)"),
WithSeverityTags("optimizer details", protocol.SeverityInformation, nil),
),
)
})
}
}
func cond[T any](cond bool, x, y T) T {
if cond {
return x
} else {
return y
}
}