Compare commits

...

8 Commits

Author SHA1 Message Date
Jeroen Ketema
91e1d28145 Go: Fix handling of generic methods in case of generic receiver base types 2026-06-30 17:20:01 +02:00
Jeroen Ketema
45e420b656 Go: Add generic method test 2026-06-30 12:20:58 +02:00
Jeroen Ketema
966f86ba57 Go: Fix findMethodOnTypeWithGivenReceiver for Go 1.27 2026-06-24 15:36:53 +02:00
Jeroen Ketema
0c12d920ab Go: Update supported versions to include 1.27 2026-06-24 12:39:14 +02:00
Jeroen Ketema
4af6feda68 Go: Add change note 2026-06-24 12:37:39 +02:00
Jeroen Ketema
f0d1d65254 Go: Update test formatting 2026-06-24 11:42:26 +02:00
Jeroen Ketema
36c7fba26d Go: Bump maxGoVersion to 1.27 2026-06-24 11:42:25 +02:00
Jeroen Ketema
076f53ea93 Go: Update to 1.27 2026-06-24 11:08:57 +02:00
15 changed files with 85 additions and 34 deletions

View File

@@ -273,7 +273,7 @@ use_repo(
)
go_sdk = use_extension("@rules_go//go:extensions.bzl", "go_sdk")
go_sdk.download(version = "1.26.4")
go_sdk.download(version = "1.27rc1")
go_deps = use_extension("@gazelle//:extensions.bzl", "go_deps")
go_deps.from_file(go_mod = "//go/extractor:go.mod")

View File

@@ -17,7 +17,7 @@
.NET 5, .NET 6, .NET 7, .NET 8, .NET 9, .NET 10","``.sln``, ``.slnx``, ``.csproj``, ``.cs``, ``.cshtml``, ``.xaml``"
GitHub Actions,"Not applicable",Not applicable,"``.github/workflows/*.yml``, ``.github/workflows/*.yaml``, ``**/action.yml``, ``**/action.yaml``"
Go (aka Golang), "Go up to 1.26", "Go 1.11 or more recent", ``.go``
Go (aka Golang), "Go up to 1.27", "Go 1.11 or more recent", ``.go``
Java,"Java 7 to 26 [6]_","javac (OpenJDK and Oracle JDK),
Eclipse compiler for Java (ECJ) [7]_",``.java``

View File

@@ -4,7 +4,7 @@ inputs:
go-test-version:
description: Which Go version to use for running the tests
required: false
default: "~1.26.4"
default: "1.27.0-rc.1"
run-code-checks:
description: Whether to run formatting, code and qhelp generation checks
required: false

View File

@@ -12,7 +12,7 @@ import (
)
var minGoVersion = util.NewSemVer("1.11")
var maxGoVersion = util.NewSemVer("1.26")
var maxGoVersion = util.NewSemVer("1.27")
type versionInfo struct {
goModVersion util.SemVer // The version of Go found in the go directive in the `go.mod` file.

View File

@@ -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
index int
}
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
@@ -530,8 +536,10 @@ 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)
typeParams := funcObj.Type().(*types.Signature).TypeParams()
populateTypeParamParents(typeParams, obj, 0)
recvTypeParams := funcObj.Type().(*types.Signature).RecvTypeParams()
populateTypeParamParents(recvTypeParams, obj, typeParams.Len())
}
// Populate type parameter parents for defined types and alias types.
if typeNameObj, ok := obj.(*types.TypeName); ok {
@@ -542,9 +550,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, 0)
} else if tp, ok := typeNameObj.Type().(*types.Alias); ok {
populateTypeParamParents(tp.TypeParams(), obj)
populateTypeParamParents(tp.TypeParams(), obj, 0)
}
}
extractObject(tw, obj, lbl)
@@ -570,8 +578,10 @@ 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)
typeParams := meth.Type().(*types.Signature).TypeParams()
populateTypeParamParents(typeParams, meth, 0)
recvTypeParams := meth.Type().(*types.Signature).RecvTypeParams()
populateTypeParamParents(recvTypeParams, meth, typeParams.Len())
extractObject(tw, meth, methlbl)
}
@@ -1660,7 +1670,8 @@ func extractType(tw *trap.Writer, tp types.Type) trap.Label {
// parent scope, so they are not dealt with by `extractScopes`
for i := 0; i < origintp.NumMethods(); i++ {
meth := origintp.Method(i).Origin()
typeParams := tp.Method(i).Type().(*types.Signature).TypeParams()
populateTypeParamParents(typeParams, meth, 0)
extractMethod(tw, meth)
}
@@ -1684,9 +1695,9 @@ func extractType(tw *trap.Writer, tp types.Type) trap.Label {
}
case *types.TypeParam:
kind = dbscheme.TypeParamType.Index()
parentlbl := getTypeParamParentLabel(tw, tp)
parentlbl, idx := 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, idx)
case *types.Union:
kind = dbscheme.TypeSetLiteral.Index()
for i := 0; i < tp.Len(); i++ {
@@ -1826,8 +1837,7 @@ 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)
idx := tp.Index()
parentlbl, idx := getTypeParamParentLabel(tw, tp)
lbl = tw.Labeler.GlobalID(fmt.Sprintf("{%v},%d,%s;typeparamtype", parentlbl, idx, tp.Obj().Name()))
case *types.Union:
var b strings.Builder
@@ -2013,10 +2023,10 @@ func extractTypeParamDecls(tw *trap.Writer, fields *ast.FieldList, parent trap.L
}
// populateTypeParamParents sets `parent` as the parent of the elements of `typeparams`
func populateTypeParamParents(typeparams *types.TypeParamList, parent types.Object) {
func populateTypeParamParents(typeparams *types.TypeParamList, parent types.Object, offset int) {
if typeparams != nil {
for idx := 0; idx < typeparams.Len(); idx++ {
setTypeParamParent(typeparams.At(idx), parent)
setTypeParamParent(typeparams.At(idx), parent, idx+offset)
}
}
}
@@ -2065,24 +2075,24 @@ func trackInstantiatedStructFields(tw *trap.Writer, tp, origintp *types.Named) {
}
}
func getTypeParamParentLabel(tw *trap.Writer, tp *types.TypeParam) trap.Label {
parent, exists := typeParamParent[tp]
func getTypeParamParentLabel(tw *trap.Writer, tp *types.TypeParam) (trap.Label, int) {
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.index
}
func setTypeParamParent(tp *types.TypeParam, newobj types.Object) {
obj, exists := typeParamParent[tp]
func setTypeParamParent(tp *types.TypeParam, newobj types.Object, idx int) {
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, idx}
} else if entry.parent != newobj || entry.index != idx {
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)
}
}

View File

@@ -1,8 +1,8 @@
module github.com/github/codeql-go/extractor
go 1.26
go 1.27
toolchain go1.26.4
toolchain go1.27rc1
// when updating this, run
// bazel run @rules_go//go -- mod tidy

View File

@@ -8,9 +8,9 @@ import (
func TestParseGoVersion(t *testing.T) {
tests := map[string]string{
"go version go1.18.9 linux/amd64": "go1.18.9",
"go version go1.26.3-X:nodwarf5 linux/amd64": "go1.26.3",
"go version go1.26.3rc1 linux/amd64": "go1.26.3rc1",
"go version go1.18.9 linux/amd64": "go1.18.9",
"go version go1.26.3-X:nodwarf5 linux/amd64": "go1.26.3",
"go version go1.26.3rc1 linux/amd64": "go1.26.3rc1",
"warning: GOPATH set to GOROOT (/usr/local/go) has no effect\ngo version go1.18.9 linux/amd64": "go1.18.9",
}
for input, expected := range tests {

View File

@@ -187,7 +187,7 @@ func findMethodOnTypeWithGivenReceiver(tp types.Type, object types.Object) *type
if definedType, ok := tp.(*types.Named); ok {
for i := 0; i < definedType.NumMethods(); i++ {
meth := definedType.Method(i)
if object == meth.Type().(*types.Signature).Recv() {
if object == meth.Type().(*types.Signature).Recv().Origin() {
return meth
}
}

View File

@@ -0,0 +1,4 @@
---
category: majorAnalysis
---
* Go 1.27 is now supported.

View File

@@ -6,3 +6,5 @@
| genericFunctions.go:45:6:45:50 | generic function instantiation expression | genericFunctions.go:45:6:45:33 | GenericFunctionTwoTypeParams | 1 | genericFunctions.go:45:43:45:49 | float64 |
| 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 |
| 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 |
| 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 |
| 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 |

View File

@@ -19,6 +19,9 @@ numberOfTypeParameters
| genericFunctions.go:150:6:150:36 | multipleAnonymousTypeParamsFunc | 3 |
| genericFunctions.go:152:6:152:36 | multipleAnonymousTypeParamsType | 3 |
| genericFunctions.go:154:51:154:51 | f | 3 |
| genericMethods.go:5:33:5:46 | GenericMethod1 | 1 |
| genericMethods.go:7:6:7:28 | StructForGenericMethod2 | 1 |
| genericMethods.go:9:36:9:49 | GenericMethod2 | 2 |
#select
| codeql-go-tests/function.EdgeConstraint | 0 | Node | interface { } |
| codeql-go-tests/function.Element | 0 | S | interface { } |
@@ -44,6 +47,10 @@ numberOfTypeParameters
| codeql-go-tests/function.New | 0 | Node | NodeConstraint |
| codeql-go-tests/function.New | 1 | Edge | EdgeConstraint |
| codeql-go-tests/function.NodeConstraint | 0 | Edge | interface { } |
| codeql-go-tests/function.StructForGenericMethod1.GenericMethod1 | 0 | P | interface { } |
| codeql-go-tests/function.StructForGenericMethod2 | 0 | P | interface { } |
| codeql-go-tests/function.StructForGenericMethod2.GenericMethod2 | 0 | Q | interface { } |
| codeql-go-tests/function.StructForGenericMethod2.GenericMethod2 | 1 | P | interface { } |
| codeql-go-tests/function.multipleAnonymousTypeParamsFunc | 0 | _ | interface { } |
| codeql-go-tests/function.multipleAnonymousTypeParamsFunc | 1 | _ | interface { string } |
| codeql-go-tests/function.multipleAnonymousTypeParamsFunc | 2 | _ | interface { } |

View File

@@ -0,0 +1,19 @@
package main
type StructForGenericMethod1 struct{}
func (*StructForGenericMethod1) GenericMethod1[P any](x P) {}
type StructForGenericMethod2[P any] struct{}
func (*StructForGenericMethod2[P]) GenericMethod2[Q any](x Q) {}
func generic_methods(s1 StructForGenericMethod1, s2 StructForGenericMethod2[int]) {
// Call the generic method specifying the type
s1.GenericMethod1[int](1)
s2.GenericMethod2[string]("hello")
// Call the generic method without specifying the type
s1.GenericMethod1("hello")
s2.GenericMethod2(42)
}

View File

@@ -11,6 +11,10 @@
| genericFunctions.go:138:29:138:40 | ShortestPath | 1 | genericFunctions.go:138:48:138:49 | to |
| genericFunctions.go:138:29:138:40 | ShortestPath | -1 | genericFunctions.go:138:7:138:7 | g |
| genericFunctions.go:154:51:154:51 | f | -1 | genericFunctions.go:154:7:154:7 | x |
| genericMethods.go:5:33:5:46 | GenericMethod1 | 0 | genericMethods.go:5:55:5:55 | x |
| genericMethods.go:9:36:9:49 | GenericMethod2 | 0 | genericMethods.go:9:58:9:58 | x |
| genericMethods.go:11:6:11:20 | generic_methods | 0 | genericMethods.go:11:22:11:23 | s1 |
| genericMethods.go:11:6:11:20 | generic_methods | 1 | genericMethods.go:11:50:11:51 | s2 |
| main.go:7:6:7:7 | f1 | 0 | main.go:7:9:7:9 | x |
| main.go:9:12:9:13 | f2 | 0 | main.go:9:15:9:15 | x |
| main.go:9:12:9:13 | f2 | 1 | main.go:9:18:9:18 | y |

View File

@@ -19,3 +19,6 @@
| genericFunctions.go:152:6:152:69 | type declaration specifier | TypeSpec | 0 | genericFunctions.go:152:38:152:42 | type parameter declaration | 0 | genericFunctions.go:152:38:152:38 | _ | genericFunctions.go:152:40:152:42 | any | interface { } |
| genericFunctions.go:152:6:152:69 | type declaration specifier | TypeSpec | 1 | genericFunctions.go:152:45:152:52 | type parameter declaration | 0 | genericFunctions.go:152:45:152:45 | _ | genericFunctions.go:152:47:152:52 | string | interface { string } |
| genericFunctions.go:152:6:152:69 | type declaration specifier | TypeSpec | 2 | genericFunctions.go:152:55:152:59 | type parameter declaration | 0 | genericFunctions.go:152:55:152:55 | _ | genericFunctions.go:152:57:152:59 | any | interface { } |
| genericMethods.go:5:1:5:61 | function declaration | MethodDecl | 0 | genericMethods.go:5:48:5:52 | type parameter declaration | 0 | genericMethods.go:5:48:5:48 | P | genericMethods.go:5:50:5:52 | any | interface { } |
| genericMethods.go:7:6:7:44 | type declaration specifier | TypeSpec | 0 | genericMethods.go:7:30:7:34 | type parameter declaration | 0 | genericMethods.go:7:30:7:30 | P | genericMethods.go:7:32:7:34 | any | interface { } |
| genericMethods.go:9:1:9:64 | function declaration | MethodDecl | 0 | genericMethods.go:9:51:9:55 | type parameter declaration | 0 | genericMethods.go:9:51:9:51 | Q | genericMethods.go:9:53:9:55 | any | interface { } |

View File

@@ -1,6 +1,8 @@
module codeql-go-tests/function
go 1.18
go 1.27
toolchain go1.27rc1
require github.com/anotherpkg v0.0.0-20200203000000-0000000000000