Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
563 changes: 563 additions & 0 deletions go/downgrades/5ff5325d274ae4f86defa195577bc7c1370b72fa/go.dbscheme

Large diffs are not rendered by default.

563 changes: 563 additions & 0 deletions go/downgrades/5ff5325d274ae4f86defa195577bc7c1370b72fa/old.dbscheme

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
class TypeParamType extends @typeparamtype {
string toString() { none() }
}

class CompositeType extends @compositetype {
string toString() { none() }
}

class TypeParamParentObject extends @typeparamparentobject {
string toString() { none() }
}

from
TypeParamType tp, string name, CompositeType bound, TypeParamParentObject parent, int idx,
boolean is_from_recv
where typeparam(tp, name, bound, parent, idx, is_from_recv)
select tp, name, bound, parent, idx
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
description: Track whether a type parameter is from a receiver
compatibility: partial
typeparam.rel: run typeparam.qlo
5 changes: 5 additions & 0 deletions go/extractor/dbscheme/dbscheme.go
Original file line number Diff line number Diff line change
Expand Up @@ -277,6 +277,11 @@ func FloatColumn(columnName string) Column {
return Column{columnName, FLOAT, false, true}
}

// BooleanColumn constructs a column with name `columnName` holding boolean values
func BooleanColumn(columnName string) Column {
return Column{columnName, BOOLEAN, false, true}
}

// A Table represents a database table
type Table struct {
name string
Expand Down
3 changes: 2 additions & 1 deletion go/extractor/dbscheme/tables.go
Original file line number Diff line number Diff line change
Expand Up @@ -1241,4 +1241,5 @@ var TypeParamTable = NewTable("typeparam",
EntityColumn(CompositeType, "bound"),
EntityColumn(TypeParamParentObjectType, "parent"),
IntColumn("idx"),
).KeySet("parent", "idx")
BooleanColumn("is_from_recv"),
).KeySet("parent", "idx", "is_from_recv")
59 changes: 36 additions & 23 deletions go/extractor/extractor.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,13 @@ import (
)

var MaxGoRoutines int
var typeParamParent map[*types.TypeParam]types.Object = make(map[*types.TypeParam]types.Object)

type typeParamParentEntry struct {
parent types.Object
isFromReceiver bool
}

var typeParamParent map[*types.TypeParam]typeParamParentEntry = make(map[*types.TypeParam]typeParamParentEntry)

func init() {
// this sets the number of threads that the Go runtime will spawn; this is separate
Expand Down Expand Up @@ -530,8 +536,7 @@ func extractObjects(tw *trap.Writer, scope *types.Scope, scopeLabel trap.Label)
// do not appear as objects in any scope, so they have to be dealt
// with separately in extractMethods.
if funcObj, ok := obj.(*types.Func); ok {
populateTypeParamParents(funcObj.Type().(*types.Signature).TypeParams(), obj)
populateTypeParamParents(funcObj.Type().(*types.Signature).RecvTypeParams(), obj)
populateTypeParamParentsFromFunction(funcObj)
}
// Populate type parameter parents for defined types and alias types.
if typeNameObj, ok := obj.(*types.TypeName); ok {
Expand All @@ -542,9 +547,9 @@ func extractObjects(tw *trap.Writer, scope *types.Scope, scopeLabel trap.Label)
// careful with alias types because before Go 1.24 they would
// return the underlying type.
if tp, ok := typeNameObj.Type().(*types.Named); ok && !typeNameObj.IsAlias() {
populateTypeParamParents(tp.TypeParams(), obj)
populateTypeParamParents(tp.TypeParams(), obj, false)
} else if tp, ok := typeNameObj.Type().(*types.Alias); ok {
populateTypeParamParents(tp.TypeParams(), obj)
populateTypeParamParents(tp.TypeParams(), obj, false)
}
}
extractObject(tw, obj, lbl)
Expand All @@ -570,8 +575,7 @@ func extractMethod(tw *trap.Writer, meth *types.Func) trap.Label {
if !exists {
// Populate type parameter parents for methods. They do not appear as
// objects in any scope, so they have to be dealt with separately here.
populateTypeParamParents(meth.Type().(*types.Signature).TypeParams(), meth)
populateTypeParamParents(meth.Type().(*types.Signature).RecvTypeParams(), meth)
populateTypeParamParentsFromFunction(meth)
extractObject(tw, meth, methlbl)
}

Expand Down Expand Up @@ -1677,9 +1681,9 @@ func extractType(tw *trap.Writer, tp types.Type) trap.Label {
}
case *types.TypeParam:
kind = dbscheme.TypeParamType.Index()
parentlbl := getTypeParamParentLabel(tw, tp)
parentlbl, isReceiverChild := getTypeParamParentLabel(tw, tp)
constraintLabel := extractType(tw, tp.Constraint())
dbscheme.TypeParamTable.Emit(tw, lbl, tp.Obj().Name(), constraintLabel, parentlbl, tp.Index())
dbscheme.TypeParamTable.Emit(tw, lbl, tp.Obj().Name(), constraintLabel, parentlbl, tp.Index(), isReceiverChild)
case *types.Union:
kind = dbscheme.TypeSetLiteral.Index()
for i := 0; i < tp.Len(); i++ {
Expand Down Expand Up @@ -1819,9 +1823,9 @@ func getTypeLabel(tw *trap.Writer, tp types.Type) (trap.Label, bool) {
}
lbl = tw.Labeler.GlobalID(fmt.Sprintf("{%s};definedtype", entitylbl))
case *types.TypeParam:
parentlbl := getTypeParamParentLabel(tw, tp)
parentlbl, isReceiverChild := getTypeParamParentLabel(tw, tp)
idx := tp.Index()
lbl = tw.Labeler.GlobalID(fmt.Sprintf("{%v},%d,%s;typeparamtype", parentlbl, idx, tp.Obj().Name()))
lbl = tw.Labeler.GlobalID(fmt.Sprintf("{%v},%d,%t,%s;typeparamtype", parentlbl, idx, isReceiverChild, tp.Obj().Name()))
case *types.Union:
var b strings.Builder
for i := 0; i < tp.Len(); i++ {
Expand Down Expand Up @@ -2005,11 +2009,20 @@ func extractTypeParamDecls(tw *trap.Writer, fields *ast.FieldList, parent trap.L
}
}

func populateTypeParamParentsFromFunction(funcObj *types.Func) {
recvTypeParams := funcObj.Type().(*types.Signature).RecvTypeParams()
populateTypeParamParents(recvTypeParams, funcObj, true)
typeParams := funcObj.Type().(*types.Signature).TypeParams()
populateTypeParamParents(typeParams, funcObj, false)
Comment on lines +2013 to +2016

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(non-blocking) I personally slightly prefer:

Suggested change
recvTypeParams := funcObj.Type().(*types.Signature).RecvTypeParams()
populateTypeParamParents(recvTypeParams, funcObj, true)
typeParams := funcObj.Type().(*types.Signature).TypeParams()
populateTypeParamParents(typeParams, funcObj, false)
signature := funcObj.Type().(*types.Signature)
populateTypeParamParents(signature.RecvTypeParams(), funcObj, true)
populateTypeParamParents(signature.TypeParams(), funcObj, false)


}

// populateTypeParamParents sets `parent` as the parent of the elements of `typeparams`
func populateTypeParamParents(typeparams *types.TypeParamList, parent types.Object) {
// and records whether elements are defined in a receiver.
func populateTypeParamParents(typeparams *types.TypeParamList, parent types.Object, isFromReceiver bool) {
if typeparams != nil {
for idx := 0; idx < typeparams.Len(); idx++ {
setTypeParamParent(typeparams.At(idx), parent)
for tparam := range typeparams.TypeParams() {
setTypeParamParent(tparam, parent, isFromReceiver)
}
}
}
Expand All @@ -2028,24 +2041,24 @@ func getObjectBeingUsed(tw *trap.Writer, ident *ast.Ident) types.Object {
}
}

func getTypeParamParentLabel(tw *trap.Writer, tp *types.TypeParam) trap.Label {
parent, exists := typeParamParent[tp]
func getTypeParamParentLabel(tw *trap.Writer, tp *types.TypeParam) (trap.Label, bool) {
entry, exists := typeParamParent[tp]
if !exists {
log.Fatalf("Parent of type parameter does not exist: %s %s", tp.String(), tp.Constraint().String())
}
parentlbl, _ := tw.Labeler.ScopedObjectID(parent, func() trap.Label {
parentlbl, _ := tw.Labeler.ScopedObjectID(entry.parent, func() trap.Label {
log.Fatalf("getTypeLabel() called for parent of type parameter %s", tp.String())
return trap.InvalidLabel
})
return parentlbl
return parentlbl, entry.isFromReceiver
}

func setTypeParamParent(tp *types.TypeParam, newobj types.Object) {
obj, exists := typeParamParent[tp]
func setTypeParamParent(tp *types.TypeParam, newobj types.Object, isFromReceiver bool) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(non-blocking) Consider renaming newobj to parent to match the field names of the new struct type.

entry, exists := typeParamParent[tp]
if !exists {
typeParamParent[tp] = newobj
} else if newobj != obj {
log.Fatalf("Parent of type parameter '%s %s' being set to a different value: '%s' vs '%s'", tp.String(), tp.Constraint().String(), obj, newobj)
typeParamParent[tp] = typeParamParentEntry{newobj, isFromReceiver}
} else if entry.parent != newobj {
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)
Comment on lines +2060 to +2061

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For completeness, we should also detect the case that entry.isFromReceiver != isFromReceiver and log it somehow. Perhaps the simplest way is to assign newEntry := typeParamParentEntry{newobj, isFromReceiver} before the conditional, then compare entry != newEntry in this conditional and log entry and newEntry instead of entry.parent and newobj.

}
}

Expand Down
2 changes: 2 additions & 0 deletions go/extractor/trap/trapwriter.go
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,8 @@ func (tw *Writer) Emit(table string, values []interface{}) error {
fmt.Fprintf(tw.wzip, "%d", value)
case float64:
fmt.Fprintf(tw.wzip, "%e", value)
case bool:
fmt.Fprintf(tw.wzip, "%t", value)
default:
return errors.New("Cannot emit value")
}
Expand Down
6 changes: 3 additions & 3 deletions go/ql/lib/go.dbscheme
Original file line number Diff line number Diff line change
Expand Up @@ -244,9 +244,9 @@ has_ellipsis(int id: @callorconversionexpr ref);

variadic(int id: @signaturetype ref);

#keyset[parent, idx]
typeparam(unique int tp: @typeparamtype ref, string name: string ref, int bound: @compositetype ref,
int parent: @typeparamparentobject ref, int idx: int ref);
#keyset[parent, idx, is_from_recv]
typeparam(unique int tp: @typeparamtype ref, string name: string ref,
int bound: @compositetype ref, int parent: @typeparamparentobject ref, int idx: int ref, boolean is_from_recv: boolean ref);

@container = @file | @folder;

Expand Down
4 changes: 4 additions & 0 deletions go/ql/lib/go.dbscheme.stats
Original file line number Diff line number Diff line change
Expand Up @@ -17791,6 +17791,10 @@
<k>idx</k>
<v>3126</v>
</e>
<e>
<k>is_from_recv</k>
<v>0</v>
</e>
</columnsizes>
<dependencies>
<dep>
Expand Down
11 changes: 7 additions & 4 deletions go/ql/lib/semmle/go/Types.qll
Original file line number Diff line number Diff line change
Expand Up @@ -382,18 +382,21 @@ class CompositeType extends @compositetype, Type { }
/** A type that comes from a type parameter. */
class TypeParamType extends @typeparamtype, CompositeType {
/** Gets the name of this type parameter type. */
string getParamName() { typeparam(this, result, _, _, _) }
string getParamName() { typeparam(this, result, _, _, _, _) }

/** Gets the constraint of this type parameter type. */
Type getConstraint() { typeparam(this, _, result, _, _) }
Type getConstraint() { typeparam(this, _, result, _, _, _) }

override InterfaceType getUnderlyingType() { result = this.getConstraint().getUnderlyingType() }

/** Gets the parent object of this type parameter type. */
TypeParamParentEntity getParent() { typeparam(this, _, _, result, _) }
TypeParamParentEntity getParent() { typeparam(this, _, _, result, _, _) }

/** Gets the index of this type parameter type. */
int getIndex() { typeparam(this, _, _, _, result) }
int getIndex() { typeparam(this, _, _, _, result, _) }

/** Holds if this type parameter type is declared as part of a receiver */
predicate isFromReceiver() { typeparam(this, _, _, _, _, true) }

override string pp() { result = this.getParamName() }

Expand Down
Loading
Loading