зеркало из https://github.com/golang/tools.git
internal/lsp: use %w in error wrappers
This fixes a bunch of fmt.Errorf calls to use %w rather than %v when wrapping an error with additional context. Change-Id: I03088376fbf89aa537555e825e5d02544d813ed2 Reviewed-on: https://go-review.googlesource.com/c/tools/+/231477 Run-TryBot: Ian Cottrell <iancottrell@google.com> TryBot-Result: Gobot Gobot <gobot@golang.org> Reviewed-by: Robert Findley <rfindley@google.com> Reviewed-by: Rebecca Stambler <rstambler@golang.org>
This commit is contained in:
Родитель
6b6965ac5d
Коммит
535e1470ec
|
@ -614,7 +614,7 @@ func (v *view) cancelBackground() {
|
|||
|
||||
func (v *view) setBuildInformation(ctx context.Context, folder span.URI, env []string, modfileFlagEnabled bool) error {
|
||||
if err := checkPathCase(folder.Filename()); err != nil {
|
||||
return fmt.Errorf("invalid workspace configuration: %v", err)
|
||||
return fmt.Errorf("invalid workspace configuration: %w", err)
|
||||
}
|
||||
// Make sure to get the `go env` before continuing with initialization.
|
||||
gomod, err := v.getGoEnv(ctx, env)
|
||||
|
|
|
@ -68,7 +68,7 @@ func (r *prepareRename) Run(ctx context.Context, args ...string) error {
|
|||
}
|
||||
result, err := conn.PrepareRename(ctx, &p)
|
||||
if err != nil {
|
||||
return fmt.Errorf("prepare_rename failed: %v", err)
|
||||
return fmt.Errorf("prepare_rename failed: %w", err)
|
||||
}
|
||||
if result == nil {
|
||||
return ErrInvalidRenamePosition
|
||||
|
|
|
@ -435,7 +435,7 @@ func (i *Instance) SetLogFile(logfile string) (func(), error) {
|
|||
}
|
||||
f, err := os.Create(logfile)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to create log file: %v", err)
|
||||
return nil, fmt.Errorf("unable to create log file: %w", err)
|
||||
}
|
||||
closeLog = func() {
|
||||
defer f.Close()
|
||||
|
|
|
@ -80,7 +80,7 @@ func NewEditor(ws *Workspace) *Editor {
|
|||
func (e *Editor) Shutdown(ctx context.Context) error {
|
||||
if e.server != nil {
|
||||
if err := e.server.Shutdown(ctx); err != nil {
|
||||
return fmt.Errorf("Shutdown: %v", err)
|
||||
return fmt.Errorf("Shutdown: %w", err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
|
@ -92,7 +92,7 @@ func (e *Editor) Exit(ctx context.Context) error {
|
|||
// Not all LSP clients issue the exit RPC, but we do so here to ensure that
|
||||
// we gracefully handle it on multi-session servers.
|
||||
if err := e.server.Exit(ctx); err != nil {
|
||||
return fmt.Errorf("Exit: %v", err)
|
||||
return fmt.Errorf("Exit: %w", err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
|
@ -130,14 +130,14 @@ func (e *Editor) initialize(ctx context.Context) error {
|
|||
if e.server != nil {
|
||||
resp, err := e.server.Initialize(ctx, params)
|
||||
if err != nil {
|
||||
return fmt.Errorf("initialize: %v", err)
|
||||
return fmt.Errorf("initialize: %w", err)
|
||||
}
|
||||
e.mu.Lock()
|
||||
e.serverCapabilities = resp.Capabilities
|
||||
e.mu.Unlock()
|
||||
|
||||
if err := e.server.Initialized(ctx, &protocol.InitializedParams{}); err != nil {
|
||||
return fmt.Errorf("initialized: %v", err)
|
||||
return fmt.Errorf("initialized: %w", err)
|
||||
}
|
||||
}
|
||||
// TODO: await initial configuration here, or expect gopls to manage that?
|
||||
|
@ -173,7 +173,7 @@ func (e *Editor) OpenFile(ctx context.Context, path string) error {
|
|||
if err := e.server.DidOpen(ctx, &protocol.DidOpenTextDocumentParams{
|
||||
TextDocument: item,
|
||||
}); err != nil {
|
||||
return fmt.Errorf("DidOpen: %v", err)
|
||||
return fmt.Errorf("DidOpen: %w", err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
|
@ -215,7 +215,7 @@ func (e *Editor) CreateBuffer(ctx context.Context, path, content string) error {
|
|||
if err := e.server.DidOpen(ctx, &protocol.DidOpenTextDocumentParams{
|
||||
TextDocument: item,
|
||||
}); err != nil {
|
||||
return fmt.Errorf("DidOpen: %v", err)
|
||||
return fmt.Errorf("DidOpen: %w", err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
|
@ -238,7 +238,7 @@ func (e *Editor) CloseBuffer(ctx context.Context, path string) error {
|
|||
URI: e.ws.URI(path),
|
||||
},
|
||||
}); err != nil {
|
||||
return fmt.Errorf("DidClose: %v", err)
|
||||
return fmt.Errorf("DidClose: %w", err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
|
@ -248,10 +248,10 @@ func (e *Editor) CloseBuffer(ctx context.Context, path string) error {
|
|||
// the filesystem.
|
||||
func (e *Editor) SaveBuffer(ctx context.Context, path string) error {
|
||||
if err := e.OrganizeImports(ctx, path); err != nil {
|
||||
return fmt.Errorf("organizing imports before save: %v", err)
|
||||
return fmt.Errorf("organizing imports before save: %w", err)
|
||||
}
|
||||
if err := e.FormatBuffer(ctx, path); err != nil {
|
||||
return fmt.Errorf("formatting before save: %v", err)
|
||||
return fmt.Errorf("formatting before save: %w", err)
|
||||
}
|
||||
|
||||
e.mu.Lock()
|
||||
|
@ -276,11 +276,11 @@ func (e *Editor) SaveBuffer(ctx context.Context, path string) error {
|
|||
TextDocument: docID,
|
||||
Reason: protocol.Manual,
|
||||
}); err != nil {
|
||||
return fmt.Errorf("WillSave: %v", err)
|
||||
return fmt.Errorf("WillSave: %w", err)
|
||||
}
|
||||
}
|
||||
if err := e.ws.WriteFile(ctx, path, content); err != nil {
|
||||
return fmt.Errorf("writing %q: %v", path, err)
|
||||
return fmt.Errorf("writing %q: %w", path, err)
|
||||
}
|
||||
if e.server != nil {
|
||||
params := &protocol.DidSaveTextDocumentParams{
|
||||
|
@ -293,7 +293,7 @@ func (e *Editor) SaveBuffer(ctx context.Context, path string) error {
|
|||
params.Text = &content
|
||||
}
|
||||
if err := e.server.DidSave(ctx, params); err != nil {
|
||||
return fmt.Errorf("DidSave: %v", err)
|
||||
return fmt.Errorf("DidSave: %w", err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
|
@ -314,7 +314,7 @@ func contentPosition(content string, offset int) (Pos, error) {
|
|||
line++
|
||||
}
|
||||
if err := scanner.Err(); err != nil {
|
||||
return Pos{}, fmt.Errorf("scanning content: %v", err)
|
||||
return Pos{}, fmt.Errorf("scanning content: %w", err)
|
||||
}
|
||||
// Scan() will drop the last line if it is empty. Correct for this.
|
||||
if strings.HasSuffix(content, "\n") && offset == start {
|
||||
|
@ -464,7 +464,7 @@ func (e *Editor) editBufferLocked(ctx context.Context, path string, edits []Edit
|
|||
}
|
||||
if e.server != nil {
|
||||
if err := e.server.DidChange(ctx, params); err != nil {
|
||||
return fmt.Errorf("DidChange: %v", err)
|
||||
return fmt.Errorf("DidChange: %w", err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
|
@ -482,7 +482,7 @@ func (e *Editor) GoToDefinition(ctx context.Context, path string, pos Pos) (stri
|
|||
|
||||
resp, err := e.server.Definition(ctx, params)
|
||||
if err != nil {
|
||||
return "", Pos{}, fmt.Errorf("definition: %v", err)
|
||||
return "", Pos{}, fmt.Errorf("definition: %w", err)
|
||||
}
|
||||
if len(resp) == 0 {
|
||||
return "", Pos{}, nil
|
||||
|
@ -490,7 +490,7 @@ func (e *Editor) GoToDefinition(ctx context.Context, path string, pos Pos) (stri
|
|||
newPath := e.ws.URIToPath(resp[0].URI)
|
||||
newPos := fromProtocolPosition(resp[0].Range.Start)
|
||||
if err := e.OpenFile(ctx, newPath); err != nil {
|
||||
return "", Pos{}, fmt.Errorf("OpenFile: %v", err)
|
||||
return "", Pos{}, fmt.Errorf("OpenFile: %w", err)
|
||||
}
|
||||
return newPath, newPos, nil
|
||||
}
|
||||
|
@ -517,7 +517,7 @@ func (e *Editor) codeAction(ctx context.Context, path string, diagnostics []prot
|
|||
}
|
||||
actions, err := e.server.CodeAction(ctx, params)
|
||||
if err != nil {
|
||||
return fmt.Errorf("textDocument/codeAction: %v", err)
|
||||
return fmt.Errorf("textDocument/codeAction: %w", err)
|
||||
}
|
||||
e.mu.Lock()
|
||||
defer e.mu.Unlock()
|
||||
|
@ -540,7 +540,7 @@ func (e *Editor) codeAction(ctx context.Context, path string, diagnostics []prot
|
|||
}
|
||||
edits := convertEdits(change.Edits)
|
||||
if err := e.editBufferLocked(ctx, path, edits); err != nil {
|
||||
return fmt.Errorf("editing buffer %q: %v", path, err)
|
||||
return fmt.Errorf("editing buffer %q: %w", path, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -567,7 +567,7 @@ func (e *Editor) FormatBuffer(ctx context.Context, path string) error {
|
|||
params.TextDocument.URI = e.ws.URI(path)
|
||||
resp, err := e.server.Formatting(ctx, params)
|
||||
if err != nil {
|
||||
return fmt.Errorf("textDocument/formatting: %v", err)
|
||||
return fmt.Errorf("textDocument/formatting: %w", err)
|
||||
}
|
||||
e.mu.Lock()
|
||||
defer e.mu.Unlock()
|
||||
|
|
|
@ -56,27 +56,27 @@ func NewWorkspace(name, srctxt, proxytxt string, env ...string) (_ *Workspace, e
|
|||
}()
|
||||
dir, err := ioutil.TempDir("", fmt.Sprintf("goplstest-ws-%s-", name))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("creating temporary workdir: %v", err)
|
||||
return nil, fmt.Errorf("creating temporary workdir: %w", err)
|
||||
}
|
||||
w.workdir = dir
|
||||
gopath, err := ioutil.TempDir("", fmt.Sprintf("goplstest-gopath-%s-", name))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("creating temporary gopath: %v", err)
|
||||
return nil, fmt.Errorf("creating temporary gopath: %w", err)
|
||||
}
|
||||
w.gopath = gopath
|
||||
files := unpackTxt(srctxt)
|
||||
for name, data := range files {
|
||||
if err := w.writeFileData(name, string(data)); err != nil {
|
||||
return nil, fmt.Errorf("writing to workdir: %v", err)
|
||||
return nil, fmt.Errorf("writing to workdir: %w", err)
|
||||
}
|
||||
}
|
||||
pd, err := ioutil.TempDir("", fmt.Sprintf("goplstest-proxy-%s-", name))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("creating temporary proxy dir: %v", err)
|
||||
return nil, fmt.Errorf("creating temporary proxy dir: %w", err)
|
||||
}
|
||||
w.proxydir = pd
|
||||
if err := writeProxyDir(unpackTxt(proxytxt), w.proxydir); err != nil {
|
||||
return nil, fmt.Errorf("writing proxy dir: %v", err)
|
||||
return nil, fmt.Errorf("writing proxy dir: %w", err)
|
||||
}
|
||||
return w, nil
|
||||
}
|
||||
|
@ -106,7 +106,7 @@ func writeProxyDir(files map[string][]byte, dir string) error {
|
|||
}
|
||||
for mv, files := range filesByModule {
|
||||
if err := proxydir.WriteModuleVersion(dir, mv.modulePath, mv.version, files); err != nil {
|
||||
return fmt.Errorf("error writing %s@%s: %v", mv.modulePath, mv.version, err)
|
||||
return fmt.Errorf("error writing %s@%s: %w", mv.modulePath, mv.version, err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
|
@ -207,7 +207,7 @@ func (w *Workspace) RegexpSearch(path string, re string) (Pos, error) {
|
|||
func (w *Workspace) RemoveFile(ctx context.Context, path string) error {
|
||||
fp := w.filePath(path)
|
||||
if err := os.Remove(fp); err != nil {
|
||||
return fmt.Errorf("removing %q: %v", path, err)
|
||||
return fmt.Errorf("removing %q: %w", path, err)
|
||||
}
|
||||
evts := []FileEvent{{
|
||||
Path: path,
|
||||
|
@ -274,7 +274,7 @@ func (w *Workspace) WriteFile(ctx context.Context, path, content string) error {
|
|||
fp := w.filePath(path)
|
||||
_, err := os.Stat(fp)
|
||||
if err != nil && !os.IsNotExist(err) {
|
||||
return fmt.Errorf("checking if %q exists: %v", path, err)
|
||||
return fmt.Errorf("checking if %q exists: %w", path, err)
|
||||
}
|
||||
var changeType protocol.FileChangeType
|
||||
if os.IsNotExist(err) {
|
||||
|
@ -299,10 +299,10 @@ func (w *Workspace) WriteFile(ctx context.Context, path, content string) error {
|
|||
func (w *Workspace) writeFileData(path string, content string) error {
|
||||
fp := w.filePath(path)
|
||||
if err := os.MkdirAll(filepath.Dir(fp), 0755); err != nil {
|
||||
return fmt.Errorf("creating nested directory: %v", err)
|
||||
return fmt.Errorf("creating nested directory: %w", err)
|
||||
}
|
||||
if err := ioutil.WriteFile(fp, []byte(content), 0644); err != nil {
|
||||
return fmt.Errorf("writing %q: %v", path, err)
|
||||
return fmt.Errorf("writing %q: %w", path, err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
@ -311,7 +311,7 @@ func (w *Workspace) removeAll() error {
|
|||
var wsErr, gopathErr, proxyErr error
|
||||
if w.gopath != "" {
|
||||
if err := w.RunGoCommand(context.Background(), "clean", "-modcache"); err != nil {
|
||||
gopathErr = fmt.Errorf("cleaning modcache: %v", err)
|
||||
gopathErr = fmt.Errorf("cleaning modcache: %w", err)
|
||||
} else {
|
||||
gopathErr = os.RemoveAll(w.gopath)
|
||||
}
|
||||
|
@ -323,7 +323,7 @@ func (w *Workspace) removeAll() error {
|
|||
proxyErr = os.RemoveAll(w.proxydir)
|
||||
}
|
||||
if wsErr != nil || gopathErr != nil || proxyErr != nil {
|
||||
return fmt.Errorf("error(s) cleaning workspace: removing workdir: %v; removing gopath: %v; removing proxy: %v", wsErr, gopathErr, proxyErr)
|
||||
return fmt.Errorf("error(s) cleaning workspace: removing workdir: %v; removing gopath: %v; removing proxy: %w", wsErr, gopathErr, proxyErr)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -18,7 +18,7 @@ var (
|
|||
func startRemoteDefault(goplsPath string, args ...string) error {
|
||||
cmd := exec.Command(goplsPath, args...)
|
||||
if err := cmd.Start(); err != nil {
|
||||
return fmt.Errorf("starting remote gopls: %v", err)
|
||||
return fmt.Errorf("starting remote gopls: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -31,7 +31,7 @@ func startRemotePosix(goplsPath string, args ...string) error {
|
|||
Setsid: true,
|
||||
}
|
||||
if err := cmd.Start(); err != nil {
|
||||
return fmt.Errorf("starting remote gopls: %v", err)
|
||||
return fmt.Errorf("starting remote gopls: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
@ -78,7 +78,7 @@ func verifyRemoteOwnershipPosix(network, address string) (bool, error) {
|
|||
if os.IsNotExist(err) {
|
||||
return true, nil
|
||||
}
|
||||
return false, fmt.Errorf("checking socket owner: %v", err)
|
||||
return false, fmt.Errorf("checking socket owner: %w", err)
|
||||
}
|
||||
stat, ok := fi.Sys().(*syscall.Stat_t)
|
||||
if !ok {
|
||||
|
@ -86,11 +86,11 @@ func verifyRemoteOwnershipPosix(network, address string) (bool, error) {
|
|||
}
|
||||
user, err := user.Current()
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("checking current user: %v", err)
|
||||
return false, fmt.Errorf("checking current user: %w", err)
|
||||
}
|
||||
uid, err := strconv.ParseUint(user.Uid, 10, 32)
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("parsing current UID: %v", err)
|
||||
return false, fmt.Errorf("parsing current UID: %w", err)
|
||||
}
|
||||
return stat.Uid == uint32(uid), nil
|
||||
}
|
||||
|
|
|
@ -225,19 +225,19 @@ func QueryServerState(ctx context.Context, network, address string) (*ServerStat
|
|||
if network == AutoNetwork {
|
||||
gp, err := os.Executable()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("getting gopls path: %v", err)
|
||||
return nil, fmt.Errorf("getting gopls path: %w", err)
|
||||
}
|
||||
network, address = autoNetworkAddress(gp, address)
|
||||
}
|
||||
netConn, err := net.DialTimeout(network, address, 5*time.Second)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("dialing remote: %v", err)
|
||||
return nil, fmt.Errorf("dialing remote: %w", err)
|
||||
}
|
||||
serverConn := jsonrpc2.NewConn(jsonrpc2.NewHeaderStream(netConn, netConn))
|
||||
go serverConn.Run(ctx, jsonrpc2.MethodNotFound)
|
||||
var state ServerState
|
||||
if err := protocol.Call(ctx, serverConn, sessionsMethod, nil, &state); err != nil {
|
||||
return nil, fmt.Errorf("querying server state: %v", err)
|
||||
return nil, fmt.Errorf("querying server state: %w", err)
|
||||
}
|
||||
return &state, nil
|
||||
}
|
||||
|
@ -250,7 +250,7 @@ func (f *Forwarder) ServeStream(ctx context.Context, stream jsonrpc2.Stream) err
|
|||
|
||||
netConn, err := f.connectToRemote(ctx)
|
||||
if err != nil {
|
||||
return fmt.Errorf("forwarder: connecting to remote: %v", err)
|
||||
return fmt.Errorf("forwarder: connecting to remote: %w", err)
|
||||
}
|
||||
serverConn := jsonrpc2.NewConn(jsonrpc2.NewHeaderStream(netConn, netConn))
|
||||
server := protocol.ServerDispatcher(serverConn)
|
||||
|
@ -353,7 +353,7 @@ func (f *Forwarder) connectToRemote(ctx context.Context) (net.Conn, error) {
|
|||
// instances are simultaneously starting up.
|
||||
if _, err := os.Stat(address); err == nil {
|
||||
if err := os.Remove(address); err != nil {
|
||||
return nil, fmt.Errorf("removing remote socket file: %v", err)
|
||||
return nil, fmt.Errorf("removing remote socket file: %w", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -366,7 +366,7 @@ func (f *Forwarder) connectToRemote(ctx context.Context) (net.Conn, error) {
|
|||
args = append(args, "-debug", f.remoteDebug)
|
||||
}
|
||||
if err := startRemote(f.goplsPath, args...); err != nil {
|
||||
return nil, fmt.Errorf("startRemote(%q, %v): %v", f.goplsPath, args, err)
|
||||
return nil, fmt.Errorf("startRemote(%q, %v): %w", f.goplsPath, args, err)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -385,7 +385,7 @@ func (f *Forwarder) connectToRemote(ctx context.Context) (net.Conn, error) {
|
|||
time.Sleep(f.dialTimeout - time.Since(startDial))
|
||||
}
|
||||
}
|
||||
return nil, fmt.Errorf("dialing remote: %v", err)
|
||||
return nil, fmt.Errorf("dialing remote: %w", err)
|
||||
}
|
||||
|
||||
// ForwarderExitFunc is used to exit the forwarder process. It is mutable for
|
||||
|
|
|
@ -432,7 +432,7 @@ func Completion(ctx context.Context, snapshot Snapshot, fh FileHandle, protoPos
|
|||
|
||||
pkg, pgh, err := getParsedFile(ctx, snapshot, fh, NarrowestPackageHandle)
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("getting file for Completion: %v", err)
|
||||
return nil, nil, fmt.Errorf("getting file for Completion: %w", err)
|
||||
}
|
||||
file, src, m, _, err := pgh.Cached()
|
||||
if err != nil {
|
||||
|
|
|
@ -24,7 +24,7 @@ func Highlight(ctx context.Context, snapshot Snapshot, fh FileHandle, pos protoc
|
|||
|
||||
pkg, pgh, err := getParsedFile(ctx, snapshot, fh, WidestPackageHandle)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("getting file for Highlight: %v", err)
|
||||
return nil, fmt.Errorf("getting file for Highlight: %w", err)
|
||||
}
|
||||
file, _, m, _, err := pgh.Parse(ctx)
|
||||
if err != nil {
|
||||
|
|
|
@ -54,7 +54,7 @@ func Identifier(ctx context.Context, snapshot Snapshot, fh FileHandle, pos proto
|
|||
|
||||
pkg, pgh, err := getParsedFile(ctx, snapshot, fh, NarrowestPackageHandle)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("getting file for Identifier: %v", err)
|
||||
return nil, fmt.Errorf("getting file for Identifier: %w", err)
|
||||
}
|
||||
file, _, m, _, err := pgh.Cached()
|
||||
if err != nil {
|
||||
|
|
|
@ -24,7 +24,7 @@ func SignatureHelp(ctx context.Context, snapshot Snapshot, fh FileHandle, pos pr
|
|||
|
||||
pkg, pgh, err := getParsedFile(ctx, snapshot, fh, NarrowestPackageHandle)
|
||||
if err != nil {
|
||||
return nil, 0, fmt.Errorf("getting file for SignatureHelp: %v", err)
|
||||
return nil, 0, fmt.Errorf("getting file for SignatureHelp: %w", err)
|
||||
}
|
||||
file, _, m, _, err := pgh.Cached()
|
||||
if err != nil {
|
||||
|
|
|
@ -20,7 +20,7 @@ func DocumentSymbols(ctx context.Context, snapshot Snapshot, fh FileHandle) ([]p
|
|||
|
||||
pkg, pgh, err := getParsedFile(ctx, snapshot, fh, NarrowestPackageHandle)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("getting file for DocumentSymbols: %v", err)
|
||||
return nil, fmt.Errorf("getting file for DocumentSymbols: %w", err)
|
||||
}
|
||||
file, _, _, _, err := pgh.Cached()
|
||||
if err != nil {
|
||||
|
|
Загрузка…
Ссылка в новой задаче