Skip to content
Draft
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
2 changes: 1 addition & 1 deletion MODULE.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -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")
Expand Down
2 changes: 1 addition & 1 deletion docs/codeql/reusables/supported-versions-compilers.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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``
Expand Down
2 changes: 1 addition & 1 deletion go/actions/test/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion go/extractor/autobuilder/build-environment.go
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
56 changes: 33 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
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
Expand Down Expand Up @@ -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())
Comment on lines +541 to +542

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.

In theory these lines shouldn't be needed here, because the comment above says that methods do not appear as objects in any scope, so we should only be dealing with top-level functions. However, rather than deleting it, I think it would be better to create a separate function for these four lines, as they are duplicated lower down. Maybe populateTypeParamParentsFromFunction(funcObj *types.Func). (Can you tell I like long method names? 😆 )

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Happy to refactor. The only thing I changed here was the addition of the offset. I simply assumed that this code was necessary (because it was already there 😄 ).

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.

Good point 😆

}
// Populate type parameter parents for defined types and alias types.
if typeNameObj, ok := obj.(*types.TypeName); ok {
Expand All @@ -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)
Expand All @@ -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)
}

Expand Down Expand Up @@ -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()

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.

Are you sure that this line wants to use the instantiated receiver type (tp instead of origintp), and the instantiated method type (tp.Method(i).Type() rather than tp.Method(i).Type().Origin())? It seems odd to assign meth as the parent in the line below when meth is different from tp.Method(i). Do we even need to populate type param parents here, as we will do something very similar in extractMethods.

Perhaps we should put a short comment here, as it will no doubt be confusing to future readers as well.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

As far as I can tell, it needs to be tp, or at least that solves the Parent of type parameter does not exist: Q any error I was seeing. I'm absolutely not sure if meth is correct here. I think I need to better understand what relations are affected by populateTypeParamParents.

populateTypeParamParents(typeParams, meth, 0)
extractMethod(tw, meth)
}

Expand All @@ -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)

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.

We should update the QLDoc for TypeParamType.getIndex() to be clear about what it means, especially as it is now different from what the go compiler libraries use. For a type param in a named type definition it is what it was before: the index in the type params for that named type. For a type param in a method definition it is now the index in the list of type params, where the type params on the method come first and the type params on the receiver come second. (Do we want this order? From left to right might make more sense.)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I'm happy to switch the order. I just kept things in the order in which the code appears for now.

case *types.Union:
kind = dbscheme.TypeSetLiteral.Index()
for i := 0; i < tp.Len(); i++ {
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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)
}
}
}
Expand Down Expand Up @@ -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)

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.

This error message is slightly inaccurate, as it only prints the objects but it could be printed if the objects are the same but the indexes are different. It's not a big thing, but consider printing the indexes as well (or the typeParamParentEntry struct.

}
}

Expand Down
4 changes: 2 additions & 2 deletions go/extractor/go.mod
Original file line number Diff line number Diff line change
@@ -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
Expand Down
6 changes: 3 additions & 3 deletions go/extractor/toolchain/toolchain_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
2 changes: 1 addition & 1 deletion go/extractor/trap/labels.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
}
Expand Down
4 changes: 4 additions & 0 deletions go/ql/lib/change-notes/2026-06-25-go-1.27.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
---
category: majorAnalysis
---
* Go 1.27 is now supported.
Original file line number Diff line number Diff line change
Expand Up @@ -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 |
Original file line number Diff line number Diff line change
Expand Up @@ -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 { } |
Expand All @@ -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 { } |
Expand Down
19 changes: 19 additions & 0 deletions go/ql/test/library-tests/semmle/go/Function/genericMethods.go
Original file line number Diff line number Diff line change
@@ -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)
}
Original file line number Diff line number Diff line change
Expand Up @@ -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 |
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 { } |
4 changes: 3 additions & 1 deletion go/ql/test/library-tests/semmle/go/Function/go.mod
Original file line number Diff line number Diff line change
@@ -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

Loading