golang.org/x/tools/gopls@v0.15.3/internal/util/astutil/purge.go (about)

     1  // Copyright 2023 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  // Package astutil provides various AST utility functions for gopls.
     6  package astutil
     7  
     8  import (
     9  	"bytes"
    10  	"go/scanner"
    11  	"go/token"
    12  
    13  	"golang.org/x/tools/gopls/internal/util/safetoken"
    14  )
    15  
    16  // PurgeFuncBodies returns a copy of src in which the contents of each
    17  // outermost {...} region except struct and interface types have been
    18  // deleted. This reduces the amount of work required to parse the
    19  // top-level declarations.
    20  //
    21  // PurgeFuncBodies does not preserve newlines or position information.
    22  // Also, if the input is invalid, parsing the output of
    23  // PurgeFuncBodies may result in a different tree due to its effects
    24  // on parser error recovery.
    25  func PurgeFuncBodies(src []byte) []byte {
    26  	// Destroy the content of any {...}-bracketed regions that are
    27  	// not immediately preceded by a "struct" or "interface"
    28  	// token.  That includes function bodies, composite literals,
    29  	// switch/select bodies, and all blocks of statements.
    30  	// This will lead to non-void functions that don't have return
    31  	// statements, which of course is a type error, but that's ok.
    32  
    33  	var out bytes.Buffer
    34  	file := token.NewFileSet().AddFile("", -1, len(src))
    35  	var sc scanner.Scanner
    36  	sc.Init(file, src, nil, 0)
    37  	var prev token.Token
    38  	var cursor int         // last consumed src offset
    39  	var braces []token.Pos // stack of unclosed braces or -1 for struct/interface type
    40  	for {
    41  		pos, tok, _ := sc.Scan()
    42  		if tok == token.EOF {
    43  			break
    44  		}
    45  		switch tok {
    46  		case token.COMMENT:
    47  			// TODO(adonovan): opt: skip, to save an estimated 20% of time.
    48  
    49  		case token.LBRACE:
    50  			if prev == token.STRUCT || prev == token.INTERFACE {
    51  				pos = -1
    52  			}
    53  			braces = append(braces, pos)
    54  
    55  		case token.RBRACE:
    56  			if last := len(braces) - 1; last >= 0 {
    57  				top := braces[last]
    58  				braces = braces[:last]
    59  				if top < 0 {
    60  					// struct/interface type: leave alone
    61  				} else if len(braces) == 0 { // toplevel only
    62  					// Delete {...} body.
    63  					start, _ := safetoken.Offset(file, top)
    64  					end, _ := safetoken.Offset(file, pos)
    65  					out.Write(src[cursor : start+len("{")])
    66  					cursor = end
    67  				}
    68  			}
    69  		}
    70  		prev = tok
    71  	}
    72  	out.Write(src[cursor:])
    73  	return out.Bytes()
    74  }