gvisor.dev/gvisor@v0.0.0-20240520182842-f9d4d51c7e0f/tools/go_generics/remove.go (about)

     1  // Copyright 2018 The gVisor Authors.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package main
    16  
    17  import (
    18  	"go/ast"
    19  	"go/token"
    20  )
    21  
    22  type typeSet map[string]struct{}
    23  
    24  // isTypeOrPointerToType determines if the given AST expression represents a
    25  // type or a pointer to a type that exists in the provided type set.
    26  func isTypeOrPointerToType(set typeSet, expr ast.Expr, starCount int) bool {
    27  	switch e := expr.(type) {
    28  	case *ast.Ident:
    29  		_, ok := set[e.Name]
    30  		return ok
    31  	case *ast.StarExpr:
    32  		if starCount > 1 {
    33  			return false
    34  		}
    35  		return isTypeOrPointerToType(set, e.X, starCount+1)
    36  	case *ast.ParenExpr:
    37  		return isTypeOrPointerToType(set, e.X, starCount)
    38  	default:
    39  		return false
    40  	}
    41  }
    42  
    43  // isMethodOf determines if the given function declaration is a method of one
    44  // of the types in the provided type set. To do that, it checks if the function
    45  // has a receiver and that its type is either T or *T, where T is a type that
    46  // exists in the set. This is per the spec:
    47  //
    48  // That parameter section must declare a single parameter, the receiver. Its
    49  // type must be of the form T or *T (possibly using parentheses) where T is a
    50  // type name. The type denoted by T is called the receiver base type; it must
    51  // not be a pointer or interface type and it must be declared in the same
    52  // package as the method.
    53  func isMethodOf(set typeSet, f *ast.FuncDecl) bool {
    54  	// If the function doesn't have exactly one receiver, then it's
    55  	// definitely not a method.
    56  	if f.Recv == nil || len(f.Recv.List) != 1 {
    57  		return false
    58  	}
    59  
    60  	return isTypeOrPointerToType(set, f.Recv.List[0].Type, 0)
    61  }
    62  
    63  // removeTypeDefinitions removes the definition of all types contained in the
    64  // provided type set.
    65  func removeTypeDefinitions(set typeSet, d *ast.GenDecl) {
    66  	if d.Tok != token.TYPE {
    67  		return
    68  	}
    69  
    70  	i := 0
    71  	for _, gs := range d.Specs {
    72  		s := gs.(*ast.TypeSpec)
    73  		if _, ok := set[s.Name.Name]; !ok {
    74  			d.Specs[i] = gs
    75  			i++
    76  		}
    77  	}
    78  
    79  	d.Specs = d.Specs[:i]
    80  }
    81  
    82  // removeTypes removes from the AST the definition of all types and their
    83  // method sets that are contained in the provided type set.
    84  func removeTypes(set typeSet, f *ast.File) {
    85  	// Go through the top-level declarations.
    86  	i := 0
    87  	for _, decl := range f.Decls {
    88  		keep := true
    89  		switch d := decl.(type) {
    90  		case *ast.GenDecl:
    91  			countBefore := len(d.Specs)
    92  			removeTypeDefinitions(set, d)
    93  			keep = countBefore == 0 || len(d.Specs) > 0
    94  		case *ast.FuncDecl:
    95  			keep = !isMethodOf(set, d)
    96  		}
    97  
    98  		if keep {
    99  			f.Decls[i] = decl
   100  			i++
   101  		}
   102  	}
   103  
   104  	f.Decls = f.Decls[:i]
   105  }