Skip to content

Commit f0842b5

Browse files
committed
Go: Fix handling of generic methods in case of generic receiver base types
1 parent 45e420b commit f0842b5

4 files changed

Lines changed: 39 additions & 26 deletions

File tree

go/extractor/extractor.go

Lines changed: 35 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,13 @@ import (
3232
)
3333

3434
var MaxGoRoutines int
35-
var typeParamParent map[*types.TypeParam]types.Object = make(map[*types.TypeParam]types.Object)
35+
36+
type typeParamParentEntry struct {
37+
parent types.Object
38+
index int
39+
}
40+
41+
var typeParamParent map[*types.TypeParam]typeParamParentEntry = make(map[*types.TypeParam]typeParamParentEntry)
3642

3743
func init() {
3844
// this sets the number of threads that the Go runtime will spawn; this is separate
@@ -530,8 +536,10 @@ func extractObjects(tw *trap.Writer, scope *types.Scope, scopeLabel trap.Label)
530536
// do not appear as objects in any scope, so they have to be dealt
531537
// with separately in extractMethods.
532538
if funcObj, ok := obj.(*types.Func); ok {
533-
populateTypeParamParents(funcObj.Type().(*types.Signature).TypeParams(), obj)
534-
populateTypeParamParents(funcObj.Type().(*types.Signature).RecvTypeParams(), obj)
539+
typeParams := funcObj.Type().(*types.Signature).TypeParams()
540+
populateTypeParamParents(typeParams, obj, 0)
541+
recvTypeParams := funcObj.Type().(*types.Signature).RecvTypeParams()
542+
populateTypeParamParents(recvTypeParams, obj, typeParams.Len())
535543
}
536544
// Populate type parameter parents for defined types and alias types.
537545
if typeNameObj, ok := obj.(*types.TypeName); ok {
@@ -542,9 +550,9 @@ func extractObjects(tw *trap.Writer, scope *types.Scope, scopeLabel trap.Label)
542550
// careful with alias types because before Go 1.24 they would
543551
// return the underlying type.
544552
if tp, ok := typeNameObj.Type().(*types.Named); ok && !typeNameObj.IsAlias() {
545-
populateTypeParamParents(tp.TypeParams(), obj)
553+
populateTypeParamParents(tp.TypeParams(), obj, 0)
546554
} else if tp, ok := typeNameObj.Type().(*types.Alias); ok {
547-
populateTypeParamParents(tp.TypeParams(), obj)
555+
populateTypeParamParents(tp.TypeParams(), obj, 0)
548556
}
549557
}
550558
extractObject(tw, obj, lbl)
@@ -570,8 +578,10 @@ func extractMethod(tw *trap.Writer, meth *types.Func) trap.Label {
570578
if !exists {
571579
// Populate type parameter parents for methods. They do not appear as
572580
// objects in any scope, so they have to be dealt with separately here.
573-
populateTypeParamParents(meth.Type().(*types.Signature).TypeParams(), meth)
574-
populateTypeParamParents(meth.Type().(*types.Signature).RecvTypeParams(), meth)
581+
typeParams := meth.Type().(*types.Signature).TypeParams()
582+
populateTypeParamParents(typeParams, meth, 0)
583+
recvTypeParams := meth.Type().(*types.Signature).RecvTypeParams()
584+
populateTypeParamParents(recvTypeParams, meth, typeParams.Len())
575585
extractObject(tw, meth, methlbl)
576586
}
577587

@@ -1660,7 +1670,10 @@ func extractType(tw *trap.Writer, tp types.Type) trap.Label {
16601670
// parent scope, so they are not dealt with by `extractScopes`
16611671
for i := 0; i < origintp.NumMethods(); i++ {
16621672
meth := origintp.Method(i).Origin()
1663-
1673+
if tp != origintp {
1674+
typeParams := tp.Method(i).Type().(*types.Signature).TypeParams()
1675+
populateTypeParamParents(typeParams, meth, 0)
1676+
}
16641677
extractMethod(tw, meth)
16651678
}
16661679

@@ -1684,9 +1697,9 @@ func extractType(tw *trap.Writer, tp types.Type) trap.Label {
16841697
}
16851698
case *types.TypeParam:
16861699
kind = dbscheme.TypeParamType.Index()
1687-
parentlbl := getTypeParamParentLabel(tw, tp)
1700+
parentlbl, idx := getTypeParamParentLabel(tw, tp)
16881701
constraintLabel := extractType(tw, tp.Constraint())
1689-
dbscheme.TypeParamTable.Emit(tw, lbl, tp.Obj().Name(), constraintLabel, parentlbl, tp.Index())
1702+
dbscheme.TypeParamTable.Emit(tw, lbl, tp.Obj().Name(), constraintLabel, parentlbl, idx)
16901703
case *types.Union:
16911704
kind = dbscheme.TypeSetLiteral.Index()
16921705
for i := 0; i < tp.Len(); i++ {
@@ -1826,8 +1839,7 @@ func getTypeLabel(tw *trap.Writer, tp types.Type) (trap.Label, bool) {
18261839
}
18271840
lbl = tw.Labeler.GlobalID(fmt.Sprintf("{%s};definedtype", entitylbl))
18281841
case *types.TypeParam:
1829-
parentlbl := getTypeParamParentLabel(tw, tp)
1830-
idx := tp.Index()
1842+
parentlbl, idx := getTypeParamParentLabel(tw, tp)
18311843
lbl = tw.Labeler.GlobalID(fmt.Sprintf("{%v},%d,%s;typeparamtype", parentlbl, idx, tp.Obj().Name()))
18321844
case *types.Union:
18331845
var b strings.Builder
@@ -2013,10 +2025,10 @@ func extractTypeParamDecls(tw *trap.Writer, fields *ast.FieldList, parent trap.L
20132025
}
20142026

20152027
// populateTypeParamParents sets `parent` as the parent of the elements of `typeparams`
2016-
func populateTypeParamParents(typeparams *types.TypeParamList, parent types.Object) {
2028+
func populateTypeParamParents(typeparams *types.TypeParamList, parent types.Object, offset int) {
20172029
if typeparams != nil {
20182030
for idx := 0; idx < typeparams.Len(); idx++ {
2019-
setTypeParamParent(typeparams.At(idx), parent)
2031+
setTypeParamParent(typeparams.At(idx), parent, idx+offset)
20202032
}
20212033
}
20222034
}
@@ -2065,24 +2077,24 @@ func trackInstantiatedStructFields(tw *trap.Writer, tp, origintp *types.Named) {
20652077
}
20662078
}
20672079

2068-
func getTypeParamParentLabel(tw *trap.Writer, tp *types.TypeParam) trap.Label {
2069-
parent, exists := typeParamParent[tp]
2080+
func getTypeParamParentLabel(tw *trap.Writer, tp *types.TypeParam) (trap.Label, int) {
2081+
entry, exists := typeParamParent[tp]
20702082
if !exists {
20712083
log.Fatalf("Parent of type parameter does not exist: %s %s", tp.String(), tp.Constraint().String())
20722084
}
2073-
parentlbl, _ := tw.Labeler.ScopedObjectID(parent, func() trap.Label {
2085+
parentlbl, _ := tw.Labeler.ScopedObjectID(entry.parent, func() trap.Label {
20742086
log.Fatalf("getTypeLabel() called for parent of type parameter %s", tp.String())
20752087
return trap.InvalidLabel
20762088
})
2077-
return parentlbl
2089+
return parentlbl, entry.index
20782090
}
20792091

2080-
func setTypeParamParent(tp *types.TypeParam, newobj types.Object) {
2081-
obj, exists := typeParamParent[tp]
2092+
func setTypeParamParent(tp *types.TypeParam, newobj types.Object, idx int) {
2093+
entry, exists := typeParamParent[tp]
20822094
if !exists {
2083-
typeParamParent[tp] = newobj
2084-
} else if newobj != obj {
2085-
log.Fatalf("Parent of type parameter '%s %s' being set to a different value: '%s' vs '%s'", tp.String(), tp.Constraint().String(), obj, newobj)
2095+
typeParamParent[tp] = typeParamParentEntry{newobj, idx}
2096+
} else if entry.parent != newobj || entry.index != idx {
2097+
log.Fatalf("Parent of type parameter '%s %s' being set to a different value: '%s' vs '%s'", tp.String(), tp.Constraint().String(), entry.parent, newobj)
20862098
}
20872099
}
20882100

go/ql/test/library-tests/semmle/go/Function/GenericFunctionInstantiationExpr.expected

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,3 +7,4 @@
77
| genericFunctions.go:141:6:141:41 | generic function instantiation expression | genericFunctions.go:141:6:141:33 | GenericFunctionInAnotherFile | 0 | genericFunctions.go:141:35:141:40 | string |
88
| genericFunctions.go:146:6:146:55 | generic function instantiation expression | genericFunctions.go:146:6:146:47 | selection of GenericFunctionInAnotherPackage | 0 | genericFunctions.go:146:49:146:54 | string |
99
| genericMethods.go:13:2:13:23 | generic function instantiation expression | genericMethods.go:13:2:13:18 | selection of GenericMethod1 | 0 | genericMethods.go:13:20:13:22 | int |
10+
| genericMethods.go:14:2:14:26 | generic function instantiation expression | genericMethods.go:14:2:14:18 | selection of GenericMethod2 | 0 | genericMethods.go:14:20:14:25 | string |

go/ql/test/library-tests/semmle/go/Function/TypeParamType.expected

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,8 +49,8 @@ numberOfTypeParameters
4949
| codeql-go-tests/function.NodeConstraint | 0 | Edge | interface { } |
5050
| codeql-go-tests/function.StructForGenericMethod1.GenericMethod1 | 0 | P | interface { } |
5151
| codeql-go-tests/function.StructForGenericMethod2 | 0 | P | interface { } |
52-
| codeql-go-tests/function.StructForGenericMethod2.GenericMethod2 | 0 | P | interface { } |
5352
| codeql-go-tests/function.StructForGenericMethod2.GenericMethod2 | 0 | Q | interface { } |
53+
| codeql-go-tests/function.StructForGenericMethod2.GenericMethod2 | 1 | P | interface { } |
5454
| codeql-go-tests/function.multipleAnonymousTypeParamsFunc | 0 | _ | interface { } |
5555
| codeql-go-tests/function.multipleAnonymousTypeParamsFunc | 1 | _ | interface { string } |
5656
| codeql-go-tests/function.multipleAnonymousTypeParamsFunc | 2 | _ | interface { } |

go/ql/test/library-tests/semmle/go/Function/genericMethods.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,9 @@ func (*StructForGenericMethod2[P]) GenericMethod2[Q any](x Q) {}
1111
func generic_methods(s1 StructForGenericMethod1, s2 StructForGenericMethod2[int]) {
1212
// Call the generic method specifying the type
1313
s1.GenericMethod1[int](1)
14-
//s2.GenericMethod2[string]("hello")
14+
s2.GenericMethod2[string]("hello")
1515

1616
// Call the generic method without specifying the type
1717
s1.GenericMethod1("hello")
18-
//s2.GenericMethod2(42)
18+
s2.GenericMethod2(42)
1919
}

0 commit comments

Comments
 (0)