Source file src/cmd/vendor/golang.org/x/tools/internal/astutil/purge.go

     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  
    14  // PurgeFuncBodies returns a copy of src in which the contents of each
    15  // outermost {...} region have been deleted, except for struct and
    16  // interface type bodies and the bodies of length-elided array
    17  // literals ([...]T), whose element count is part of the type. It
    18  // includes function bodies, function-literal bodies, and the bodies
    19  // of slice, map, and explicitly-sized array composite literals (whose
    20  // contents don't affect the type of the enclosing declaration). This
    21  // reduces the amount of work required to parse the top-level
    22  // declarations.
    23  //
    24  // PurgeFuncBodies does not preserve newlines or position information.
    25  // Also, if the input is invalid, parsing the output of
    26  // PurgeFuncBodies may result in a different tree due to its effects
    27  // on parser error recovery.
    28  func PurgeFuncBodies(src []byte) []byte {
    29  	// Destroy the content of any {...}-bracketed regions that are
    30  	// not immediately preceded by a "struct" or "interface" token,
    31  	// and that are not the body of a length-elided array literal.
    32  	// That includes function bodies, switch/select bodies, and most
    33  	// composite literals; this will lead to non-void functions that
    34  	// don't have return statements, which of course is a type error,
    35  	// but that's ok.
    36  
    37  	var out bytes.Buffer
    38  	file := token.NewFileSet().AddFile("", -1, len(src))
    39  	var sc scanner.Scanner
    40  	sc.Init(file, src, nil, 0)
    41  	var prev token.Token
    42  	var cursor int         // last consumed src offset
    43  	var braces []token.Pos // stack of unclosed braces, or -1 for a region we preserve
    44  	var ellipsis bool      // saw "[...]" not yet consumed by a literal-body "{"
    45  	for {
    46  		pos, tok, _ := sc.Scan()
    47  		if tok == token.EOF {
    48  			break
    49  		}
    50  		switch tok {
    51  		case token.COMMENT:
    52  			// TODO(adonovan): opt: skip, to save an estimated 20% of time.
    53  
    54  		case token.SEMICOLON:
    55  			ellipsis = false
    56  
    57  		case token.RBRACK:
    58  			// "...]" occurs only in the array-type prefix of a
    59  			// composite literal; variadic "..." is followed by
    60  			// a type or ")", never "]".
    61  			if prev == token.ELLIPSIS {
    62  				ellipsis = true
    63  			}
    64  
    65  		case token.LBRACE:
    66  			if prev == token.STRUCT || prev == token.INTERFACE {
    67  				pos = -1 // type body: preserve (don't consume ellipsis)
    68  			} else if ellipsis {
    69  				pos = -1 // [...]T literal body: preserve
    70  				ellipsis = false
    71  			}
    72  			braces = append(braces, pos)
    73  
    74  		case token.RBRACE:
    75  			if last := len(braces) - 1; last >= 0 {
    76  				top := braces[last]
    77  				braces = braces[:last]
    78  				if top < 0 {
    79  					// preserve
    80  				} else if len(braces) == 0 { // toplevel only
    81  					// Delete {...} body.
    82  					start := file.Offset(top)
    83  					end := file.Offset(pos)
    84  					out.Write(src[cursor : start+len("{")])
    85  					cursor = end
    86  				}
    87  			}
    88  		}
    89  		prev = tok
    90  	}
    91  	out.Write(src[cursor:])
    92  	return out.Bytes()
    93  }
    94  

View as plain text