diff --git a/go/ql/lib/semmle/go/Scopes.qll b/go/ql/lib/semmle/go/Scopes.qll index 04cb65fa987e..7386b81868fc 100644 --- a/go/ql/lib/semmle/go/Scopes.qll +++ b/go/ql/lib/semmle/go/Scopes.qll @@ -197,8 +197,11 @@ class PackageEntity extends Entity, @pkgobject { } /** A built-in or declared named type. */ class TypeEntity extends Entity, @typeobject { } +/** The parent of a type parameter type, either a declared type or a declared function. */ +class TypeParamParentEntity extends Entity, @typeparamparentobject { } + /** A declared named type. */ -class DeclaredType extends TypeEntity, DeclaredEntity, @decltypeobject { +class DeclaredType extends TypeEntity, DeclaredEntity, TypeParamParentEntity, @decltypeobject { /** Gets the declaration specifier declaring this type. */ TypeSpec getSpec() { result.getNameExpr() = this.getDeclaration() } } @@ -598,7 +601,7 @@ class PromotedMethod extends Method { } /** A declared function. */ -class DeclaredFunction extends Function, DeclaredEntity, @declfunctionobject { +class DeclaredFunction extends Function, DeclaredEntity, TypeParamParentEntity, @declfunctionobject { override FuncDecl getFuncDecl() { result.getNameExpr() = this.getDeclaration() } override predicate mayHaveSideEffects() { diff --git a/go/ql/lib/semmle/go/Types.qll b/go/ql/lib/semmle/go/Types.qll index 026b009aa3fa..645f0e3fe67e 100644 --- a/go/ql/lib/semmle/go/Types.qll +++ b/go/ql/lib/semmle/go/Types.qll @@ -381,6 +381,12 @@ class TypeParamType extends @typeparamtype, CompositeType { override InterfaceType getUnderlyingType() { result = this.getConstraint().getUnderlyingType() } + /** Gets the parent object of this type parameter type. */ + TypeParamParentEntity getParent() { typeparam(this, _, _, result, _) } + + /** Gets the index of this type parameter type. */ + int getIndex() { typeparam(this, _, _, _, result) } + override string pp() { result = this.getParamName() } /** diff --git a/go/ql/test/library-tests/semmle/go/Function/TypeParamType.expected b/go/ql/test/library-tests/semmle/go/Function/TypeParamType.expected index bb614a4b41c5..49a00ade7ab9 100644 --- a/go/ql/test/library-tests/semmle/go/Function/TypeParamType.expected +++ b/go/ql/test/library-tests/semmle/go/Function/TypeParamType.expected @@ -1,30 +1,161 @@ -| E | Ordered | -| E | comparable | -| E | interface { } | -| E1 | interface { } | -| E2 | interface { } | -| Edge | EdgeConstraint | -| Edge | interface { } | -| F | floaty | -| K | comparable | -| Node | NodeConstraint | -| Node | interface { } | -| S | interface { } | -| S | interface { ~[]E } | -| S1 | interface { ~[]E1 } | -| S2 | interface { ~[]E2 } | -| SF2 | interface { } | -| SG2 | interface { } | -| T | Ordered | -| T | comparable | -| T | interface { string \| []uint8 } | -| T | interface { } | -| T1 | interface { } | -| T2 | interface { } | -| TF1 | interface { } | -| TF2 | interface { } | -| TG1 | interface { } | -| TG2 | interface { } | -| U | interface { } | -| V | interface { int64 \| float64 } | -| bytes | interface { []uint8 \| string } | +| cmp.Compare | 0 | T | Ordered | +| cmp.Less | 0 | T | Ordered | +| cmp.Or | 0 | T | comparable | +| cmp.isNaN | 0 | T | Ordered | +| codeql-go-tests/function.EdgeConstraint | 0 | Node | interface { } | +| codeql-go-tests/function.Element | 0 | S | interface { } | +| codeql-go-tests/function.GenericFunctionInAnotherFile | 0 | T | interface { } | +| codeql-go-tests/function.GenericFunctionOneTypeParam | 0 | T | interface { } | +| codeql-go-tests/function.GenericFunctionTwoTypeParams | 0 | K | comparable | +| codeql-go-tests/function.GenericFunctionTwoTypeParams | 1 | V | interface { int64 \| float64 } | +| codeql-go-tests/function.GenericStruct1 | 0 | T | interface { } | +| codeql-go-tests/function.GenericStruct1.f1 | 0 | TF1 | interface { } | +| codeql-go-tests/function.GenericStruct1.g1 | 0 | TG1 | interface { } | +| codeql-go-tests/function.GenericStruct2 | 0 | S | interface { } | +| codeql-go-tests/function.GenericStruct2 | 1 | T | interface { } | +| codeql-go-tests/function.GenericStruct2.f2 | 0 | SF2 | interface { } | +| codeql-go-tests/function.GenericStruct2.f2 | 1 | TF2 | interface { } | +| codeql-go-tests/function.GenericStruct2.g2 | 0 | SG2 | interface { } | +| codeql-go-tests/function.GenericStruct2.g2 | 1 | TG2 | interface { } | +| codeql-go-tests/function.Graph | 0 | Node | NodeConstraint | +| codeql-go-tests/function.Graph | 1 | Edge | EdgeConstraint | +| codeql-go-tests/function.Graph.ShortestPath | 0 | Node | NodeConstraint | +| codeql-go-tests/function.Graph.ShortestPath | 1 | Edge | EdgeConstraint | +| codeql-go-tests/function.List | 0 | T | interface { } | +| codeql-go-tests/function.List.MyLen | 0 | U | interface { } | +| codeql-go-tests/function.New | 0 | Node | NodeConstraint | +| codeql-go-tests/function.New | 1 | Edge | EdgeConstraint | +| codeql-go-tests/function.NodeConstraint | 0 | Edge | interface { } | +| github.com/anotherpkg.GenericFunctionInAnotherPackage | 0 | T | interface { } | +| internal/bytealg.HashStr | 0 | T | interface { string \| []uint8 } | +| internal/bytealg.HashStrRev | 0 | T | interface { string \| []uint8 } | +| internal/bytealg.IndexRabinKarp | 0 | T | interface { string \| []uint8 } | +| internal/bytealg.LastIndexRabinKarp | 0 | T | interface { string \| []uint8 } | +| runtime.fandbits | 0 | F | floaty | +| runtime.fmax | 0 | F | floaty | +| runtime.fmin | 0 | F | floaty | +| runtime.forbits | 0 | F | floaty | +| runtime.noEscapePtr | 0 | T | interface { } | +| runtime/internal/atomic.Pointer.CompareAndSwap | 0 | T | interface { } | +| runtime/internal/atomic.Pointer.CompareAndSwapNoWB | 0 | T | interface { } | +| runtime/internal/atomic.Pointer.Load | 0 | T | interface { } | +| runtime/internal/atomic.Pointer.Store | 0 | T | interface { } | +| runtime/internal/atomic.Pointer.StoreNoWB | 0 | T | interface { } | +| slices.BinarySearch | 0 | S | interface { ~[]E } | +| slices.BinarySearch | 1 | E | Ordered | +| slices.BinarySearchFunc | 0 | S | interface { ~[]E } | +| slices.BinarySearchFunc | 1 | E | interface { } | +| slices.BinarySearchFunc | 2 | T | interface { } | +| slices.Clip | 0 | S | interface { ~[]E } | +| slices.Clip | 1 | E | interface { } | +| slices.Clone | 0 | S | interface { ~[]E } | +| slices.Clone | 1 | E | interface { } | +| slices.Compact | 0 | S | interface { ~[]E } | +| slices.Compact | 1 | E | comparable | +| slices.CompactFunc | 0 | S | interface { ~[]E } | +| slices.CompactFunc | 1 | E | interface { } | +| slices.Compare | 0 | S | interface { ~[]E } | +| slices.Compare | 1 | E | Ordered | +| slices.CompareFunc | 0 | S1 | interface { ~[]E1 } | +| slices.CompareFunc | 1 | S2 | interface { ~[]E2 } | +| slices.CompareFunc | 2 | E1 | interface { } | +| slices.CompareFunc | 3 | E2 | interface { } | +| slices.Concat | 0 | S | interface { ~[]E } | +| slices.Concat | 1 | E | interface { } | +| slices.Contains | 0 | S | interface { ~[]E } | +| slices.Contains | 1 | E | comparable | +| slices.ContainsFunc | 0 | S | interface { ~[]E } | +| slices.ContainsFunc | 1 | E | interface { } | +| slices.Delete | 0 | S | interface { ~[]E } | +| slices.Delete | 1 | E | interface { } | +| slices.DeleteFunc | 0 | S | interface { ~[]E } | +| slices.DeleteFunc | 1 | E | interface { } | +| slices.Equal | 0 | S | interface { ~[]E } | +| slices.Equal | 1 | E | comparable | +| slices.EqualFunc | 0 | S1 | interface { ~[]E1 } | +| slices.EqualFunc | 1 | S2 | interface { ~[]E2 } | +| slices.EqualFunc | 2 | E1 | interface { } | +| slices.EqualFunc | 3 | E2 | interface { } | +| slices.Grow | 0 | S | interface { ~[]E } | +| slices.Grow | 1 | E | interface { } | +| slices.Index | 0 | S | interface { ~[]E } | +| slices.Index | 1 | E | comparable | +| slices.IndexFunc | 0 | S | interface { ~[]E } | +| slices.IndexFunc | 1 | E | interface { } | +| slices.Insert | 0 | S | interface { ~[]E } | +| slices.Insert | 1 | E | interface { } | +| slices.IsSorted | 0 | S | interface { ~[]E } | +| slices.IsSorted | 1 | E | Ordered | +| slices.IsSortedFunc | 0 | S | interface { ~[]E } | +| slices.IsSortedFunc | 1 | E | interface { } | +| slices.Max | 0 | S | interface { ~[]E } | +| slices.Max | 1 | E | Ordered | +| slices.MaxFunc | 0 | S | interface { ~[]E } | +| slices.MaxFunc | 1 | E | interface { } | +| slices.Min | 0 | S | interface { ~[]E } | +| slices.Min | 1 | E | Ordered | +| slices.MinFunc | 0 | S | interface { ~[]E } | +| slices.MinFunc | 1 | E | interface { } | +| slices.Replace | 0 | S | interface { ~[]E } | +| slices.Replace | 1 | E | interface { } | +| slices.Reverse | 0 | S | interface { ~[]E } | +| slices.Reverse | 1 | E | interface { } | +| slices.Sort | 0 | S | interface { ~[]E } | +| slices.Sort | 1 | E | Ordered | +| slices.SortFunc | 0 | S | interface { ~[]E } | +| slices.SortFunc | 1 | E | interface { } | +| slices.SortStableFunc | 0 | S | interface { ~[]E } | +| slices.SortStableFunc | 1 | E | interface { } | +| slices.breakPatternsCmpFunc | 0 | E | interface { } | +| slices.breakPatternsOrdered | 0 | E | Ordered | +| slices.choosePivotCmpFunc | 0 | E | interface { } | +| slices.choosePivotOrdered | 0 | E | Ordered | +| slices.heapSortCmpFunc | 0 | E | interface { } | +| slices.heapSortOrdered | 0 | E | Ordered | +| slices.insertionSortCmpFunc | 0 | E | interface { } | +| slices.insertionSortOrdered | 0 | E | Ordered | +| slices.isNaN | 0 | T | Ordered | +| slices.medianAdjacentCmpFunc | 0 | E | interface { } | +| slices.medianAdjacentOrdered | 0 | E | Ordered | +| slices.medianCmpFunc | 0 | E | interface { } | +| slices.medianOrdered | 0 | E | Ordered | +| slices.order2CmpFunc | 0 | E | interface { } | +| slices.order2Ordered | 0 | E | Ordered | +| slices.overlaps | 0 | E | interface { } | +| slices.partialInsertionSortCmpFunc | 0 | E | interface { } | +| slices.partialInsertionSortOrdered | 0 | E | Ordered | +| slices.partitionCmpFunc | 0 | E | interface { } | +| slices.partitionEqualCmpFunc | 0 | E | interface { } | +| slices.partitionEqualOrdered | 0 | E | Ordered | +| slices.partitionOrdered | 0 | E | Ordered | +| slices.pdqsortCmpFunc | 0 | E | interface { } | +| slices.pdqsortOrdered | 0 | E | Ordered | +| slices.reverseRangeCmpFunc | 0 | E | interface { } | +| slices.reverseRangeOrdered | 0 | E | Ordered | +| slices.rotateCmpFunc | 0 | E | interface { } | +| slices.rotateLeft | 0 | E | interface { } | +| slices.rotateOrdered | 0 | E | Ordered | +| slices.rotateRight | 0 | E | interface { } | +| slices.siftDownCmpFunc | 0 | E | interface { } | +| slices.siftDownOrdered | 0 | E | Ordered | +| slices.stableCmpFunc | 0 | E | interface { } | +| slices.stableOrdered | 0 | E | Ordered | +| slices.startIdx | 0 | E | interface { } | +| slices.swap | 0 | E | interface { } | +| slices.swapRangeCmpFunc | 0 | E | interface { } | +| slices.swapRangeOrdered | 0 | E | Ordered | +| slices.symMergeCmpFunc | 0 | E | interface { } | +| slices.symMergeOrdered | 0 | E | Ordered | +| sync.OnceValue | 0 | T | interface { } | +| sync.OnceValues | 0 | T1 | interface { } | +| sync.OnceValues | 1 | T2 | interface { } | +| sync/atomic.Pointer | 0 | T | interface { } | +| sync/atomic.Pointer.CompareAndSwap | 0 | T | interface { } | +| sync/atomic.Pointer.Load | 0 | T | interface { } | +| sync/atomic.Pointer.Store | 0 | T | interface { } | +| sync/atomic.Pointer.Swap | 0 | T | interface { } | +| time.atoi | 0 | bytes | interface { []uint8 \| string } | +| time.isDigit | 0 | bytes | interface { []uint8 \| string } | +| time.leadingInt | 0 | bytes | interface { []uint8 \| string } | +| time.parseNanoseconds | 0 | bytes | interface { []uint8 \| string } | +| time.parseRFC3339 | 0 | bytes | interface { []uint8 \| string } | diff --git a/go/ql/test/library-tests/semmle/go/Function/TypeParamType.ql b/go/ql/test/library-tests/semmle/go/Function/TypeParamType.ql index a4167f8d7022..9170c0d35ed0 100644 --- a/go/ql/test/library-tests/semmle/go/Function/TypeParamType.ql +++ b/go/ql/test/library-tests/semmle/go/Function/TypeParamType.ql @@ -1,4 +1,5 @@ import go -from TypeParamType tpt -select tpt.getParamName(), tpt.getConstraint().pp() +from TypeParamType tpt, TypeParamParentEntity ty +where ty = tpt.getParent() +select ty.getQualifiedName(), tpt.getIndex(), tpt.getParamName(), tpt.getConstraint().pp()