Close gaps in public boundary checks

This commit is contained in:
2026-09-22 11:40:25 +00:00
parent 06251e385e
commit e5870453f9
2 changed files with 153 additions and 16 deletions

View File

@@ -63,6 +63,55 @@ import "example.test/facade/internal/hidden"
type privateState struct { value hidden.Value }
type Public struct { state privateState }
type Option interface { apply(*privateState) }
`,
},
{
name: "returned private type method is rejected",
source: `package facade
import "example.test/facade/internal/hidden"
type result struct{}
func (result) Leak() hidden.Value { return hidden.Value{} }
func New() result { return result{} }
`,
wantReject: true,
},
{
name: "reachable private pointer method is rejected",
source: `package facade
import "example.test/facade/internal/hidden"
type result struct{}
func (*result) Leak() hidden.Value { return hidden.Value{} }
type Public struct { Result result }
`,
wantReject: true,
},
{
name: "internal generic argument is rejected",
source: `package facade
import "example.test/facade/internal/hidden"
type Box[T any] struct{}
func New() Box[hidden.Value] { return Box[hidden.Value]{} }
`,
wantReject: true,
},
{
name: "internal alias argument is rejected",
source: `package facade
import "example.test/facade/internal/hidden"
type Box[T any] = struct{}
func New() Box[hidden.Value] { return Box[hidden.Value]{} }
`,
wantReject: true,
},
{
name: "public generic argument and recursive methods are allowed",
source: `package facade
type Value struct{}
type Box[T any] struct{}
func New() Box[Value] { return Box[Value]{} }
type result struct{}
func NewResult() result { return result{} }
func (*result) Next() *result { return nil }
`,
},
{
@@ -178,6 +227,8 @@ func publicSurfaceViolations(pkg *types.Package, modulePath string) []string {
var inspect func(types.Type, string)
var inspectTuple func(*types.Tuple, string)
var inspectTypeParams func(*types.TypeParamList, string)
var inspectTypeArgs func(*types.TypeList, string)
var inspectMethods func(*types.Named, string)
inspect = func(typ types.Type, where string) {
if typ == nil || seen[typ] {
return
@@ -186,6 +237,7 @@ func publicSurfaceViolations(pkg *types.Package, modulePath string) []string {
switch value := typ.(type) {
case *types.Named:
inspectTypeArgs(value.TypeArgs(), where)
if isInternalPackage(value.Obj().Pkg(), modulePath) {
record(where, value)
return
@@ -193,8 +245,10 @@ func publicSurfaceViolations(pkg *types.Package, modulePath string) []string {
if value.Obj().Pkg() == pkg {
inspectTypeParams(value.TypeParams(), where)
inspect(value.Underlying(), where)
inspectMethods(value, where)
}
case *types.Alias:
inspectTypeArgs(value.TypeArgs(), where)
if isInternalPackage(value.Obj().Pkg(), modulePath) {
record(where, value)
return
@@ -258,21 +312,12 @@ func publicSurfaceViolations(pkg *types.Package, modulePath string) []string {
inspect(params.At(index), where)
}
}
for _, name := range pkg.Scope().Names() {
if !token.IsExported(name) {
continue
}
object := pkg.Scope().Lookup(name)
inspect(object.Type(), name)
typeName, ok := object.(*types.TypeName)
if !ok {
continue
}
named, ok := types.Unalias(typeName.Type()).(*types.Named)
if !ok {
continue
inspectTypeArgs = func(args *types.TypeList, where string) {
for index := 0; index < args.Len(); index++ {
inspect(args.At(index), where)
}
}
inspectMethods = func(named *types.Named, where string) {
for _, methodSet := range []*types.MethodSet{types.NewMethodSet(named), types.NewMethodSet(types.NewPointer(named))} {
for index := 0; index < methodSet.Len(); index++ {
selection := methodSet.At(index)
@@ -281,13 +326,19 @@ func publicSurfaceViolations(pkg *types.Package, modulePath string) []string {
continue
}
if isInternalPackage(method.Pkg(), modulePath) {
record(name+"."+method.Name(), method.Type())
record(where+"."+method.Name(), selection.Type())
continue
}
inspect(method.Type(), name+"."+method.Name())
inspect(selection.Type(), where+"."+method.Name())
}
}
}
for _, name := range pkg.Scope().Names() {
if token.IsExported(name) {
inspect(pkg.Scope().Lookup(name).Type(), name)
}
}
sort.Strings(violations)
return compactStrings(violations)
}