106 lines
2.9 KiB
Go
106 lines
2.9 KiB
Go
// Copyright 2018 The gVisor Authors.
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
package main
|
|
|
|
import (
|
|
"go/ast"
|
|
"go/token"
|
|
)
|
|
|
|
type typeSet map[string]struct{}
|
|
|
|
// isTypeOrPointerToType determines if the given AST expression represents a
|
|
// type or a pointer to a type that exists in the provided type set.
|
|
func isTypeOrPointerToType(set typeSet, expr ast.Expr, starCount int) bool {
|
|
switch e := expr.(type) {
|
|
case *ast.Ident:
|
|
_, ok := set[e.Name]
|
|
return ok
|
|
case *ast.StarExpr:
|
|
if starCount > 1 {
|
|
return false
|
|
}
|
|
return isTypeOrPointerToType(set, e.X, starCount+1)
|
|
case *ast.ParenExpr:
|
|
return isTypeOrPointerToType(set, e.X, starCount)
|
|
default:
|
|
return false
|
|
}
|
|
}
|
|
|
|
// isMethodOf determines if the given function declaration is a method of one
|
|
// of the types in the provided type set. To do that, it checks if the function
|
|
// has a receiver and that its type is either T or *T, where T is a type that
|
|
// exists in the set. This is per the spec:
|
|
//
|
|
// That parameter section must declare a single parameter, the receiver. Its
|
|
// type must be of the form T or *T (possibly using parentheses) where T is a
|
|
// type name. The type denoted by T is called the receiver base type; it must
|
|
// not be a pointer or interface type and it must be declared in the same
|
|
// package as the method.
|
|
func isMethodOf(set typeSet, f *ast.FuncDecl) bool {
|
|
// If the function doesn't have exactly one receiver, then it's
|
|
// definitely not a method.
|
|
if f.Recv == nil || len(f.Recv.List) != 1 {
|
|
return false
|
|
}
|
|
|
|
return isTypeOrPointerToType(set, f.Recv.List[0].Type, 0)
|
|
}
|
|
|
|
// removeTypeDefinitions removes the definition of all types contained in the
|
|
// provided type set.
|
|
func removeTypeDefinitions(set typeSet, d *ast.GenDecl) {
|
|
if d.Tok != token.TYPE {
|
|
return
|
|
}
|
|
|
|
i := 0
|
|
for _, gs := range d.Specs {
|
|
s := gs.(*ast.TypeSpec)
|
|
if _, ok := set[s.Name.Name]; !ok {
|
|
d.Specs[i] = gs
|
|
i++
|
|
}
|
|
}
|
|
|
|
d.Specs = d.Specs[:i]
|
|
}
|
|
|
|
// removeTypes removes from the AST the definition of all types and their
|
|
// method sets that are contained in the provided type set.
|
|
func removeTypes(set typeSet, f *ast.File) {
|
|
// Go through the top-level declarations.
|
|
i := 0
|
|
for _, decl := range f.Decls {
|
|
keep := true
|
|
switch d := decl.(type) {
|
|
case *ast.GenDecl:
|
|
countBefore := len(d.Specs)
|
|
removeTypeDefinitions(set, d)
|
|
keep = countBefore == 0 || len(d.Specs) > 0
|
|
case *ast.FuncDecl:
|
|
keep = !isMethodOf(set, d)
|
|
}
|
|
|
|
if keep {
|
|
f.Decls[i] = decl
|
|
i++
|
|
}
|
|
}
|
|
|
|
f.Decls = f.Decls[:i]
|
|
}
|