| // Copyright 2023 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 astutil provides various AST utility functions for gopls. |
| package astutil |
| |
| import ( |
| "bytes" |
| "go/scanner" |
| "go/token" |
| |
| "golang.org/x/tools/gopls/internal/util/safetoken" |
| ) |
| |
| // PurgeFuncBodies returns a copy of src in which the contents of each |
| // outermost {...} region except struct and interface types have been |
| // deleted. This reduces the amount of work required to parse the |
| // top-level declarations. |
| // |
| // PurgeFuncBodies does not preserve newlines or position information. |
| // Also, if the input is invalid, parsing the output of |
| // PurgeFuncBodies may result in a different tree due to its effects |
| // on parser error recovery. |
| func PurgeFuncBodies(src []byte) []byte { |
| // Destroy the content of any {...}-bracketed regions that are |
| // not immediately preceded by a "struct" or "interface" |
| // token. That includes function bodies, composite literals, |
| // switch/select bodies, and all blocks of statements. |
| // This will lead to non-void functions that don't have return |
| // statements, which of course is a type error, but that's ok. |
| |
| var out bytes.Buffer |
| file := token.NewFileSet().AddFile("", -1, len(src)) |
| var sc scanner.Scanner |
| sc.Init(file, src, nil, 0) |
| var prev token.Token |
| var cursor int // last consumed src offset |
| var braces []token.Pos // stack of unclosed braces or -1 for struct/interface type |
| for { |
| pos, tok, _ := sc.Scan() |
| if tok == token.EOF { |
| break |
| } |
| switch tok { |
| case token.COMMENT: |
| // TODO(adonovan): opt: skip, to save an estimated 20% of time. |
| |
| case token.LBRACE: |
| if prev == token.STRUCT || prev == token.INTERFACE { |
| pos = -1 |
| } |
| braces = append(braces, pos) |
| |
| case token.RBRACE: |
| if last := len(braces) - 1; last >= 0 { |
| top := braces[last] |
| braces = braces[:last] |
| if top < 0 { |
| // struct/interface type: leave alone |
| } else if len(braces) == 0 { // toplevel only |
| // Delete {...} body. |
| start, _ := safetoken.Offset(file, top) |
| end, _ := safetoken.Offset(file, pos) |
| out.Write(src[cursor : start+len("{")]) |
| cursor = end |
| } |
| } |
| } |
| prev = tok |
| } |
| out.Write(src[cursor:]) |
| return out.Bytes() |
| } |