From 7f0a37913f8096e2327c537bf182763d648ddd6d Mon Sep 17 00:00:00 2001 From: Owen Mansel-Chan Date: Thu, 27 Jan 2022 15:03:04 +0000 Subject: [PATCH 01/49] Use Go 1.18 --- .github/workflows/codeqltest.yml | 12 ++++++------ go.mod | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/.github/workflows/codeqltest.yml b/.github/workflows/codeqltest.yml index e728473fdcf..e07d5f443b3 100644 --- a/.github/workflows/codeqltest.yml +++ b/.github/workflows/codeqltest.yml @@ -7,10 +7,10 @@ jobs: runs-on: ubuntu-latest steps: - - name: Set up Go 1.17 + - name: Set up Go 1.18 uses: actions/setup-go@v1 with: - go-version: 1.17 + go-version: 1.18 id: go - name: Set up CodeQL CLI @@ -59,10 +59,10 @@ jobs: name: Test MacOS runs-on: macOS-latest steps: - - name: Set up Go 1.17 + - name: Set up Go 1.18 uses: actions/setup-go@v1 with: - go-version: 1.17 + go-version: 1.18 id: go - name: Set up CodeQL CLI @@ -99,10 +99,10 @@ jobs: name: Test Windows runs-on: windows-2019 steps: - - name: Set up Go 1.17 + - name: Set up Go 1.18 uses: actions/setup-go@v1 with: - go-version: 1.17 + go-version: 1.18 id: go - name: Set up CodeQL CLI diff --git a/go.mod b/go.mod index 5d77091a50c..25e2d87d342 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module github.com/github/codeql-go -go 1.17 +go 1.18 require ( golang.org/x/mod v0.5.0 From f7dcb118166a54d23dd28f433e376e6990ee5b08 Mon Sep 17 00:00:00 2001 From: Owen Mansel-Chan Date: Mon, 21 Feb 2022 15:48:53 +0000 Subject: [PATCH 02/49] Extract uninstantiated generic types only --- extractor/extractor.go | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/extractor/extractor.go b/extractor/extractor.go index 3fa29569bf1..a9964a79593 100644 --- a/extractor/extractor.go +++ b/extractor/extractor.go @@ -1434,24 +1434,25 @@ func extractType(tw *trap.Writer, tp types.Type) trap.Label { kind = dbscheme.ChanTypes[tp.Dir()].Index() extractElementType(tw, lbl, tp.Elem()) case *types.Named: + origintp := tp.Origin() kind = dbscheme.NamedType.Index() - dbscheme.TypeNameTable.Emit(tw, lbl, tp.Obj().Name()) - underlying := tp.Underlying() + dbscheme.TypeNameTable.Emit(tw, lbl, origintp.Obj().Name()) + underlying := origintp.Underlying() extractUnderlyingType(tw, lbl, underlying) - entitylbl, exists := tw.Labeler.LookupObjectID(tp.Obj(), lbl) + entitylbl, exists := tw.Labeler.LookupObjectID(origintp.Obj(), lbl) if entitylbl == trap.InvalidLabel { - log.Printf("Omitting type-object binding for unknown object %v.\n", tp.Obj()) + log.Printf("Omitting type-object binding for unknown object %v.\n", origintp.Obj()) } else { if !exists { - extractObject(tw, tp.Obj(), entitylbl) + extractObject(tw, origintp.Obj(), entitylbl) } dbscheme.TypeObjectTable.Emit(tw, lbl, entitylbl) } // ensure all methods have labels - for i := 0; i < tp.NumMethods(); i++ { - meth := tp.Method(i) + for i := 0; i < origintp.NumMethods(); i++ { + meth := origintp.Method(i) extractMethod(tw, meth) } @@ -1568,12 +1569,13 @@ func getTypeLabel(tw *trap.Writer, tp types.Type) (trap.Label, bool) { elem := extractType(tw, tp.Elem()) lbl = tw.Labeler.GlobalID(fmt.Sprintf("%v,{%s};chantype", dir, elem)) case *types.Named: - entitylbl, exists := tw.Labeler.LookupObjectID(tp.Obj(), lbl) + origintp := tp.Origin() + entitylbl, exists := tw.Labeler.LookupObjectID(origintp.Obj(), lbl) if entitylbl == trap.InvalidLabel { - panic(fmt.Sprintf("Cannot construct label for named type %v (underlying object is %v).\n", tp, tp.Obj())) + panic(fmt.Sprintf("Cannot construct label for named type %v (underlying object is %v).\n", origintp, origintp.Obj())) } if !exists { - extractObject(tw, tp.Obj(), entitylbl) + extractObject(tw, origintp.Obj(), entitylbl) } lbl = tw.Labeler.GlobalID(fmt.Sprintf("{%s};namedtype", entitylbl)) } From 3952b1c07a8d684b5f134034e44df8ae951b11ca Mon Sep 17 00:00:00 2001 From: Owen Mansel-Chan Date: Thu, 27 Jan 2022 14:57:52 +0000 Subject: [PATCH 03/49] Extract type parameter types (and update dbscheme) --- extractor/dbscheme/tables.go | 9 +++++++++ extractor/extractor.go | 8 ++++++++ ql/lib/go.dbscheme | 30 +++++++++++++++++------------- 3 files changed, 34 insertions(+), 13 deletions(-) diff --git a/extractor/dbscheme/tables.go b/extractor/dbscheme/tables.go index 1f7a31e988d..f934ca57bb5 100644 --- a/extractor/dbscheme/tables.go +++ b/extractor/dbscheme/tables.go @@ -790,6 +790,9 @@ var BasicTypes = map[gotypes.BasicKind]*BranchType{ // CompositeType is the type of all composite (that is, non-basic) types var CompositeType = NewUnionType("@compositetype") +// TypeParamType is the type of type parameter types +var TypeParamType = TypeKind.NewBranch("@typeparamtype", CompositeType) + // ElementContainerType is the type of types that have elements, such as arrays // and channels var ElementContainerType = NewUnionType("@containertype", CompositeType) @@ -1171,3 +1174,9 @@ var HasEllipsisTable = NewTable("has_ellipsis", var VariadicTable = NewTable("variadic", EntityColumn(SignatureType, "id"), ) + +var TypeParamTable = NewTable("typeparam", + EntityColumn(TypeParamType, "tp").Unique(), + StringColumn("name"), + EntityColumn(CompositeType, "bound"), +) diff --git a/extractor/extractor.go b/extractor/extractor.go index a9964a79593..40ab6a8592d 100644 --- a/extractor/extractor.go +++ b/extractor/extractor.go @@ -1464,6 +1464,9 @@ func extractType(tw *trap.Writer, tp types.Type) trap.Label { dbscheme.MethodHostsTable.Emit(tw, methlbl, lbl) } } + case *types.TypeParam: + kind = dbscheme.TypeParamType.Index() + dbscheme.TypeParamTable.Emit(tw, lbl, tp.Obj().Name(), extractType(tw, tp.Constraint())) default: log.Fatalf("unexpected type %T", tp) } @@ -1578,6 +1581,11 @@ func getTypeLabel(tw *trap.Writer, tp types.Type) (trap.Label, bool) { extractObject(tw, origintp.Obj(), entitylbl) } lbl = tw.Labeler.GlobalID(fmt.Sprintf("{%s};namedtype", entitylbl)) + case *types.TypeParam: + constraint := extractType(tw, tp.Constraint()) + lbl = tw.Labeler.GlobalID(fmt.Sprintf("{%s},{%s};typeparamtype", tp.Obj().Name(), constraint)) + default: + log.Fatalf("(getTypeLabel) unexpected type %T", tp) } tw.Labeler.TypeLabels[tp] = lbl } diff --git a/ql/lib/go.dbscheme b/ql/lib/go.dbscheme index 8f168c8af3f..e0ac0aab04f 100644 --- a/ql/lib/go.dbscheme +++ b/ql/lib/go.dbscheme @@ -225,6 +225,8 @@ has_ellipsis(int id: @callorconversionexpr ref); variadic(int id: @signaturetype ref); +typeparam(unique int tp: @typeparamtype ref, string name: string ref, int bound: @compositetype ref); + @container = @file | @folder; @locatable = @xmllocatable | @node | @localscope; @@ -476,18 +478,19 @@ case @type.kind of | 23 = @complexliteraltype | 24 = @stringliteraltype | 25 = @nilliteraltype -| 26 = @arraytype -| 27 = @slicetype -| 28 = @structtype -| 29 = @pointertype -| 30 = @interfacetype -| 31 = @tupletype -| 32 = @signaturetype -| 33 = @maptype -| 34 = @sendchantype -| 35 = @recvchantype -| 36 = @sendrcvchantype -| 37 = @namedtype; +| 26 = @typeparamtype +| 27 = @arraytype +| 28 = @slicetype +| 29 = @structtype +| 30 = @pointertype +| 31 = @interfacetype +| 32 = @tupletype +| 33 = @signaturetype +| 34 = @maptype +| 35 = @sendchantype +| 36 = @recvchantype +| 37 = @sendrcvchantype +| 38 = @namedtype; @basictype = @booltype | @numerictype | @stringtype | @literaltype | @invalidtype | @unsafepointertype; @@ -510,7 +513,8 @@ case @type.kind of @literaltype = @boolliteraltype | @intliteraltype | @runeliteraltype | @floatliteraltype | @complexliteraltype | @stringliteraltype | @nilliteraltype; -@compositetype = @containertype | @structtype | @pointertype | @interfacetype | @tupletype | @signaturetype | @namedtype; +@compositetype = @typeparamtype | @containertype | @structtype | @pointertype | @interfacetype | @tupletype + | @signaturetype | @namedtype; @containertype = @arraytype | @slicetype | @maptype | @chantype; From b8ab46d9693547b8c3b59085223a28b5dac0beec Mon Sep 17 00:00:00 2001 From: Owen Mansel-Chan Date: Wed, 9 Feb 2022 14:38:29 +0000 Subject: [PATCH 04/49] Add QL class for type parameter types --- ql/lib/semmle/go/Types.qll | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/ql/lib/semmle/go/Types.qll b/ql/lib/semmle/go/Types.qll index abc7d9ed4a8..5d3df3f35af 100644 --- a/ql/lib/semmle/go/Types.qll +++ b/ql/lib/semmle/go/Types.qll @@ -306,6 +306,26 @@ class NilLiteralType extends @nilliteraltype, LiteralType { /** A composite type, that is, not a basic type. */ 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, _) } + + /** Gets the constraint of this type parameter type. */ + Type getConstraint() { typeparam(this, _, result) } + + override InterfaceType getUnderlyingType() { result = this.getConstraint().getUnderlyingType() } + + override string pp() { result = this.getParamName() } + + /** + * Gets a pretty-printed representation of this type including its constraint. + */ + string ppWithConstraint() { result = this.getParamName() + " " + this.getConstraint().pp() } + + override string toString() { result = "type parameter type" } +} + /** An array type. */ class ArrayType extends @arraytype, CompositeType { /** Gets the element type of this array type. */ From e84db95f9c32807d98672feb5bc1124299a95dfd Mon Sep 17 00:00:00 2001 From: Owen Mansel-Chan Date: Thu, 27 Jan 2022 15:01:17 +0000 Subject: [PATCH 05/49] Extract generic function instantiations to new table --- extractor/dbscheme/tables.go | 8 +++- extractor/extractor.go | 17 +++++++- ql/lib/go.dbscheme | 81 ++++++++++++++++++------------------ ql/lib/semmle/go/Expr.qll | 42 ++++++++++++++++++- 4 files changed, 104 insertions(+), 44 deletions(-) diff --git a/extractor/dbscheme/tables.go b/extractor/dbscheme/tables.go index f934ca57bb5..b541b178316 100644 --- a/extractor/dbscheme/tables.go +++ b/extractor/dbscheme/tables.go @@ -371,9 +371,15 @@ var ParenExpr = ExprKind.NewBranch("@parenexpr") // SelectorExpr is the type of selector expression AST nodes var SelectorExpr = ExprKind.NewBranch("@selectorexpr") -// IndexExpr is the type of index expression AST nodes +// IndexExpr is the type of index expression AST nodes which are not generic type +// instantiation expressions var IndexExpr = ExprKind.NewBranch("@indexexpr") +// GenericFunctionInstantiationExpr is the type of AST nodes that represent a instantiation +// of a generic type. These correspond to some index expression AST nodes and all index +// list expression AST nodes. +var GenericFunctionInstantiationExpr = ExprKind.NewBranch("@genericfunctioninstantiationexpr") + // SliceExpr is the type of slice expression AST nodes var SliceExpr = ExprKind.NewBranch("@sliceexpr") diff --git a/extractor/extractor.go b/extractor/extractor.go index 40ab6a8592d..c0eb6c32154 100644 --- a/extractor/extractor.go +++ b/extractor/extractor.go @@ -877,9 +877,24 @@ func extractExpr(tw *trap.Writer, expr ast.Expr, parent trap.Label, idx int) { if expr == nil { return } - kind = dbscheme.IndexExpr.Index() + switch tp := tw.Package.TypesInfo.TypeOf(expr.X).Underlying().(type) { + case *types.Signature: + kind = dbscheme.GenericFunctionInstantiationExpr.Index() + case *types.Map, *types.Array, *types.Slice, *types.Basic, *types.Pointer: + // map, array, slice, string or pointer to array + kind = dbscheme.IndexExpr.Index() + default: + log.Fatalf("unsupported IndexExpr: its base expression is a %s.", tp.String()) + } extractExpr(tw, expr.X, lbl, 0) extractExpr(tw, expr.Index, lbl, 1) + case *ast.IndexListExpr: + if expr == nil { + return + } + kind = dbscheme.GenericFunctionInstantiationExpr.Index() + extractExpr(tw, expr.X, lbl, 0) + extractExprs(tw, expr.Indices, lbl, 1, 1) case *ast.SliceExpr: if expr == nil { return diff --git a/ql/lib/go.dbscheme b/ql/lib/go.dbscheme index e0ac0aab04f..e0d5be4b1ca 100644 --- a/ql/lib/go.dbscheme +++ b/ql/lib/go.dbscheme @@ -272,46 +272,47 @@ case @expr.kind of | 10 = @parenexpr | 11 = @selectorexpr | 12 = @indexexpr -| 13 = @sliceexpr -| 14 = @typeassertexpr -| 15 = @callorconversionexpr -| 16 = @starexpr -| 17 = @keyvalueexpr -| 18 = @arraytypeexpr -| 19 = @structtypeexpr -| 20 = @functypeexpr -| 21 = @interfacetypeexpr -| 22 = @maptypeexpr -| 23 = @plusexpr -| 24 = @minusexpr -| 25 = @notexpr -| 26 = @complementexpr -| 27 = @derefexpr -| 28 = @addressexpr -| 29 = @arrowexpr -| 30 = @lorexpr -| 31 = @landexpr -| 32 = @eqlexpr -| 33 = @neqexpr -| 34 = @lssexpr -| 35 = @leqexpr -| 36 = @gtrexpr -| 37 = @geqexpr -| 38 = @addexpr -| 39 = @subexpr -| 40 = @orexpr -| 41 = @xorexpr -| 42 = @mulexpr -| 43 = @quoexpr -| 44 = @remexpr -| 45 = @shlexpr -| 46 = @shrexpr -| 47 = @andexpr -| 48 = @andnotexpr -| 49 = @sendchantypeexpr -| 50 = @recvchantypeexpr -| 51 = @sendrcvchantypeexpr -| 52 = @errorexpr; +| 13 = @genericfunctioninstantiationexpr +| 14 = @sliceexpr +| 15 = @typeassertexpr +| 16 = @callorconversionexpr +| 17 = @starexpr +| 18 = @keyvalueexpr +| 19 = @arraytypeexpr +| 20 = @structtypeexpr +| 21 = @functypeexpr +| 22 = @interfacetypeexpr +| 23 = @maptypeexpr +| 24 = @plusexpr +| 25 = @minusexpr +| 26 = @notexpr +| 27 = @complementexpr +| 28 = @derefexpr +| 29 = @addressexpr +| 30 = @arrowexpr +| 31 = @lorexpr +| 32 = @landexpr +| 33 = @eqlexpr +| 34 = @neqexpr +| 35 = @lssexpr +| 36 = @leqexpr +| 37 = @gtrexpr +| 38 = @geqexpr +| 39 = @addexpr +| 40 = @subexpr +| 41 = @orexpr +| 42 = @xorexpr +| 43 = @mulexpr +| 44 = @quoexpr +| 45 = @remexpr +| 46 = @shlexpr +| 47 = @shrexpr +| 48 = @andexpr +| 49 = @andnotexpr +| 50 = @sendchantypeexpr +| 51 = @recvchantypeexpr +| 52 = @sendrcvchantypeexpr +| 53 = @errorexpr; @basiclit = @intlit | @floatlit | @imaglit | @charlit | @stringlit; diff --git a/ql/lib/semmle/go/Expr.qll b/ql/lib/semmle/go/Expr.qll index 4bddc6fc744..013b209637b 100644 --- a/ql/lib/semmle/go/Expr.qll +++ b/ql/lib/semmle/go/Expr.qll @@ -626,11 +626,16 @@ class PromotedSelector extends SelectorExpr { /** * An index expression, that is, a base expression followed by an index. + * Expressions which represent generic type instantiations have been + * excluded. * * Examples: * * ```go - * a[i] + * array[i] + * arrayptr[i] + * slice[i] + * map[key] * ``` */ class IndexExpr extends @indexexpr, Expr { @@ -647,6 +652,34 @@ class IndexExpr extends @indexexpr, Expr { override string getAPrimaryQlClass() { result = "IndexExpr" } } +/** + * A generic function instantiation, that is, a base expression that represents + * a generic function, followed by a list of type arguments. + * + * Examples: + * + * ```go + * genericfunction[type] + * genericfunction[type1, type2] + * ``` + */ +class GenericFunctionInstantiationExpr extends @genericfunctioninstantiationexpr, Expr { + /** Gets the generic type expression. */ + Expr getBase() { result = this.getChildExpr(0) } + + /** Gets the `i`th type argument. */ + Expr getTypeArgument(int i) { + i >= 0 and + result = this.getChildExpr(i + 1) + } + + override predicate mayHaveOwnSideEffects() { any() } + + override string toString() { result = "generic function instantiation expression" } + + override string getAPrimaryQlClass() { result = "GenericFunctionInstantiationExpr" } +} + /** * A slice expression, that is, a base expression followed by slice indices. * @@ -797,11 +830,16 @@ class CallExpr extends CallOrConversionExpr { result = callee.(Ident).getName() or result = callee.(SelectorExpr).getSelector().getName() + or + result = callee.(GenericFunctionInstantiationExpr).getBase().(Ident).getName() ) } /** Gets the declared target of this call. */ - Function getTarget() { this.getCalleeExpr() = result.getAReference() } + Function getTarget() { + this.getCalleeExpr() = result.getAReference() or + this.getCalleeExpr().(GenericFunctionInstantiationExpr).getBase() = result.getAReference() + } /** Holds if this call has an ellipsis after its last argument. */ predicate hasEllipsis() { has_ellipsis(this) } From a05a525755b24b6a892477b8668e16813454ebed Mon Sep 17 00:00:00 2001 From: Owen Mansel-Chan Date: Tue, 15 Feb 2022 13:11:16 +0000 Subject: [PATCH 06/49] Extract generic type instantiations to new table --- extractor/dbscheme/tables.go | 10 ++++- extractor/extractor.go | 17 ++++---- ql/lib/go.dbscheme | 81 ++++++++++++++++++------------------ ql/lib/semmle/go/Expr.qll | 75 ++++++++++++++++++++++++++++----- 4 files changed, 124 insertions(+), 59 deletions(-) diff --git a/extractor/dbscheme/tables.go b/extractor/dbscheme/tables.go index b541b178316..0f86932cf4a 100644 --- a/extractor/dbscheme/tables.go +++ b/extractor/dbscheme/tables.go @@ -4,6 +4,7 @@ import ( "go/ast" "go/token" gotypes "go/types" + "golang.org/x/tools/go/packages" ) @@ -371,8 +372,8 @@ var ParenExpr = ExprKind.NewBranch("@parenexpr") // SelectorExpr is the type of selector expression AST nodes var SelectorExpr = ExprKind.NewBranch("@selectorexpr") -// IndexExpr is the type of index expression AST nodes which are not generic type -// instantiation expressions +// IndexExpr is the type of AST nodes for index expressions and generic type +// instantiation expressions with one type argument var IndexExpr = ExprKind.NewBranch("@indexexpr") // GenericFunctionInstantiationExpr is the type of AST nodes that represent a instantiation @@ -380,6 +381,11 @@ var IndexExpr = ExprKind.NewBranch("@indexexpr") // list expression AST nodes. var GenericFunctionInstantiationExpr = ExprKind.NewBranch("@genericfunctioninstantiationexpr") +// GenericTypeInstantiationExpr is the type of AST nodes that represent an instantiation +// of a generic type. These correspond to some index expression AST nodes and all index +// list expression AST nodes. +var GenericTypeInstantiationExpr = ExprKind.NewBranch("@generictypeinstantiationexpr") + // SliceExpr is the type of slice expression AST nodes var SliceExpr = ExprKind.NewBranch("@sliceexpr") diff --git a/extractor/extractor.go b/extractor/extractor.go index c0eb6c32154..8a92237bd12 100644 --- a/extractor/extractor.go +++ b/extractor/extractor.go @@ -877,14 +877,13 @@ func extractExpr(tw *trap.Writer, expr ast.Expr, parent trap.Label, idx int) { if expr == nil { return } - switch tp := tw.Package.TypesInfo.TypeOf(expr.X).Underlying().(type) { - case *types.Signature: + if _, ok := tw.Package.TypesInfo.TypeOf(expr.X).Underlying().(*types.Signature); ok { kind = dbscheme.GenericFunctionInstantiationExpr.Index() - case *types.Map, *types.Array, *types.Slice, *types.Basic, *types.Pointer: - // map, array, slice, string or pointer to array + } else { + // Can't distinguish between actual index expressions (into a map, + // array, slice, string or pointer to array) and generic type + // specialization expression, so we do it later in QL. kind = dbscheme.IndexExpr.Index() - default: - log.Fatalf("unsupported IndexExpr: its base expression is a %s.", tp.String()) } extractExpr(tw, expr.X, lbl, 0) extractExpr(tw, expr.Index, lbl, 1) @@ -892,7 +891,11 @@ func extractExpr(tw *trap.Writer, expr ast.Expr, parent trap.Label, idx int) { if expr == nil { return } - kind = dbscheme.GenericFunctionInstantiationExpr.Index() + if _, ok := tw.Package.TypesInfo.TypeOf(expr.X).Underlying().(*types.Signature); ok { + kind = dbscheme.GenericFunctionInstantiationExpr.Index() + } else { + kind = dbscheme.GenericTypeInstantiationExpr.Index() + } extractExpr(tw, expr.X, lbl, 0) extractExprs(tw, expr.Indices, lbl, 1, 1) case *ast.SliceExpr: diff --git a/ql/lib/go.dbscheme b/ql/lib/go.dbscheme index e0d5be4b1ca..ba44b5feecc 100644 --- a/ql/lib/go.dbscheme +++ b/ql/lib/go.dbscheme @@ -273,46 +273,47 @@ case @expr.kind of | 11 = @selectorexpr | 12 = @indexexpr | 13 = @genericfunctioninstantiationexpr -| 14 = @sliceexpr -| 15 = @typeassertexpr -| 16 = @callorconversionexpr -| 17 = @starexpr -| 18 = @keyvalueexpr -| 19 = @arraytypeexpr -| 20 = @structtypeexpr -| 21 = @functypeexpr -| 22 = @interfacetypeexpr -| 23 = @maptypeexpr -| 24 = @plusexpr -| 25 = @minusexpr -| 26 = @notexpr -| 27 = @complementexpr -| 28 = @derefexpr -| 29 = @addressexpr -| 30 = @arrowexpr -| 31 = @lorexpr -| 32 = @landexpr -| 33 = @eqlexpr -| 34 = @neqexpr -| 35 = @lssexpr -| 36 = @leqexpr -| 37 = @gtrexpr -| 38 = @geqexpr -| 39 = @addexpr -| 40 = @subexpr -| 41 = @orexpr -| 42 = @xorexpr -| 43 = @mulexpr -| 44 = @quoexpr -| 45 = @remexpr -| 46 = @shlexpr -| 47 = @shrexpr -| 48 = @andexpr -| 49 = @andnotexpr -| 50 = @sendchantypeexpr -| 51 = @recvchantypeexpr -| 52 = @sendrcvchantypeexpr -| 53 = @errorexpr; +| 14 = @generictypeinstantiationexpr +| 15 = @sliceexpr +| 16 = @typeassertexpr +| 17 = @callorconversionexpr +| 18 = @starexpr +| 19 = @keyvalueexpr +| 20 = @arraytypeexpr +| 21 = @structtypeexpr +| 22 = @functypeexpr +| 23 = @interfacetypeexpr +| 24 = @maptypeexpr +| 25 = @plusexpr +| 26 = @minusexpr +| 27 = @notexpr +| 28 = @complementexpr +| 29 = @derefexpr +| 30 = @addressexpr +| 31 = @arrowexpr +| 32 = @lorexpr +| 33 = @landexpr +| 34 = @eqlexpr +| 35 = @neqexpr +| 36 = @lssexpr +| 37 = @leqexpr +| 38 = @gtrexpr +| 39 = @geqexpr +| 40 = @addexpr +| 41 = @subexpr +| 42 = @orexpr +| 43 = @xorexpr +| 44 = @mulexpr +| 45 = @quoexpr +| 46 = @remexpr +| 47 = @shlexpr +| 48 = @shrexpr +| 49 = @andexpr +| 50 = @andnotexpr +| 51 = @sendchantypeexpr +| 52 = @recvchantypeexpr +| 53 = @sendrcvchantypeexpr +| 54 = @errorexpr; @basiclit = @intlit | @floatlit | @imaglit | @charlit | @stringlit; diff --git a/ql/lib/semmle/go/Expr.qll b/ql/lib/semmle/go/Expr.qll index 013b209637b..db3956693e3 100644 --- a/ql/lib/semmle/go/Expr.qll +++ b/ql/lib/semmle/go/Expr.qll @@ -639,6 +639,8 @@ class PromotedSelector extends SelectorExpr { * ``` */ class IndexExpr extends @indexexpr, Expr { + IndexExpr() { not isTypeExprBottomUp(this.getChildExpr(0)) } + /** Gets the base of this index expression. */ Expr getBase() { result = this.getChildExpr(0) } @@ -664,7 +666,7 @@ class IndexExpr extends @indexexpr, Expr { * ``` */ class GenericFunctionInstantiationExpr extends @genericfunctioninstantiationexpr, Expr { - /** Gets the generic type expression. */ + /** Gets the generic function expression. */ Expr getBase() { result = this.getChildExpr(0) } /** Gets the `i`th type argument. */ @@ -680,6 +682,40 @@ class GenericFunctionInstantiationExpr extends @genericfunctioninstantiationexpr override string getAPrimaryQlClass() { result = "GenericFunctionInstantiationExpr" } } +/** + * A generic type instantiation, that is, a base expression that is a generic + * type followed by a list of type arguments. + * + * Examples: + * + * ```go + * generictype[type] + * generictype[type1, type2] + * ``` + */ +class GenericTypeInstantiationExpr extends Expr { + GenericTypeInstantiationExpr() { + this instanceof @generictypeinstantiationexpr + or + this instanceof @indexexpr and isTypeExprBottomUp(this.getChildExpr(0)) + } + + /** Gets the generic type expression. */ + Expr getBase() { result = this.getChildExpr(0) } + + /** Gets the `i`th type argument. */ + Expr getTypeArgument(int i) { + i >= 0 and + result = this.getChildExpr(i + 1) + } + + override predicate mayHaveOwnSideEffects() { any() } + + override string toString() { result = "generic type instantiation expression" } + + override string getAPrimaryQlClass() { result = "GenericTypeInstantiationExpr" } +} + /** * A slice expression, that is, a base expression followed by slice indices. * @@ -1994,15 +2030,30 @@ class LabelName extends Name { * a bottom-up analysis. In such cases, `isTypeExprTopDown` below is useful. */ private predicate isTypeExprBottomUp(Expr e) { - e instanceof TypeName or - e instanceof @arraytypeexpr or - e instanceof @structtypeexpr or - e instanceof @functypeexpr or - e instanceof @interfacetypeexpr or - e instanceof @maptypeexpr or - e instanceof @chantypeexpr or - isTypeExprBottomUp(e.(ParenExpr).getExpr()) or - isTypeExprBottomUp(e.(StarExpr).getBase()) or + e instanceof TypeName + or + e instanceof @arraytypeexpr + or + e instanceof @structtypeexpr + or + e instanceof @functypeexpr + or + e instanceof @interfacetypeexpr + or + e instanceof @maptypeexpr + or + e instanceof @chantypeexpr + or + e instanceof @genericfunctioninstantiationexpr + or + e instanceof @generictypeinstantiationexpr + or + e instanceof @indexexpr and isTypeExprBottomUp(e.getChildExpr(0)) + or + isTypeExprBottomUp(e.(ParenExpr).getExpr()) + or + isTypeExprBottomUp(e.(StarExpr).getBase()) + or isTypeExprBottomUp(e.(Ellipsis).getOperand()) } @@ -2043,6 +2094,10 @@ private predicate isTypeExprTopDown(Expr e) { or e = any(TypeSpec s).getTypeExpr() or + e = any(GenericTypeInstantiationExpr gtie).getBase() + or + e = any(GenericTypeInstantiationExpr gtie).getTypeArgument(_) + or e = any(TypeSwitchStmt s).getACase().getExpr(_) and // special case: `nil` is allowed in a type case but isn't a type not e = Builtin::nil().getAReference() From 3510f2cdcde5799757a76a6e1b2b3e4052c04296 Mon Sep 17 00:00:00 2001 From: Owen Mansel-Chan Date: Tue, 29 Mar 2022 12:28:49 +0100 Subject: [PATCH 07/49] Support non-basic interfaces in extractor --- extractor/dbscheme/tables.go | 6 + extractor/extractor.go | 142 ++++++++++++++-- extractor/trap/trapwriter.go | 17 +- ql/lib/go.dbscheme | 66 +++---- ql/lib/semmle/go/Expr.qll | 18 ++ ql/lib/semmle/go/Types.qll | 321 ++++++++++++++++++++++++++++++++--- 6 files changed, 494 insertions(+), 76 deletions(-) diff --git a/extractor/dbscheme/tables.go b/extractor/dbscheme/tables.go index 0f86932cf4a..6bf253a103f 100644 --- a/extractor/dbscheme/tables.go +++ b/extractor/dbscheme/tables.go @@ -465,6 +465,9 @@ var InterfaceTypeExpr = ExprKind.NewBranch("@interfacetypeexpr", FieldParentType // MapTypeExpr is the type of map type AST nodes var MapTypeExpr = ExprKind.NewBranch("@maptypeexpr") +// TypeSetLiteralExpr is the type of type set literal type AST nodes +var TypeSetLiteralExpr = ExprKind.NewBranch("@typesetliteralexpr") + // ChanTypeExpr is the type of channel type AST nodes var ChanTypeExpr = NewUnionType("@chantypeexpr") @@ -846,6 +849,9 @@ var ChanTypes = map[gotypes.ChanDir]*BranchType{ // NamedType is the type of named types var NamedType = TypeKind.NewBranch("@namedtype", CompositeType) +// TypeSetLiteral is the type of type set literals +var TypeSetLiteral = TypeKind.NewBranch("@typesetliteraltype", CompositeType) + // PackageType is the type of packages var PackageType = NewPrimaryKeyType("@package") diff --git a/extractor/extractor.go b/extractor/extractor.go index 8a92237bd12..29a272515b3 100644 --- a/extractor/extractor.go +++ b/extractor/extractor.go @@ -877,7 +877,7 @@ func extractExpr(tw *trap.Writer, expr ast.Expr, parent trap.Label, idx int) { if expr == nil { return } - if _, ok := tw.Package.TypesInfo.TypeOf(expr.X).Underlying().(*types.Signature); ok { + if _, ok := typeOf(tw, expr.X).Underlying().(*types.Signature); ok { kind = dbscheme.GenericFunctionInstantiationExpr.Index() } else { // Can't distinguish between actual index expressions (into a map, @@ -891,7 +891,7 @@ func extractExpr(tw *trap.Writer, expr ast.Expr, parent trap.Label, idx int) { if expr == nil { return } - if _, ok := tw.Package.TypesInfo.TypeOf(expr.X).Underlying().(*types.Signature); ok { + if _, ok := typeOf(tw, expr.X).Underlying().(*types.Signature); ok { kind = dbscheme.GenericFunctionInstantiationExpr.Index() } else { kind = dbscheme.GenericTypeInstantiationExpr.Index() @@ -941,23 +941,34 @@ func extractExpr(tw *trap.Writer, expr ast.Expr, parent trap.Label, idx int) { if expr == nil { return } - tp := dbscheme.UnaryExprs[expr.Op] - if tp == nil { - log.Fatalf("unsupported unary operator %s", expr.Op) + if expr.Op == token.TILDE { + kind = dbscheme.TypeSetLiteralExpr.Index() + } else { + tp := dbscheme.UnaryExprs[expr.Op] + if tp == nil { + log.Fatalf("unsupported unary operator %s", expr.Op) + } + kind = tp.Index() } - kind = tp.Index() extractExpr(tw, expr.X, lbl, 0) case *ast.BinaryExpr: if expr == nil { return } - tp := dbscheme.BinaryExprs[expr.Op] - if tp == nil { - log.Fatalf("unsupported binary operator %s", expr.Op) + _, isUnionType := typeOf(tw, expr).(*types.Union) + if expr.Op == token.OR && isUnionType { + kind = dbscheme.TypeSetLiteralExpr.Index() + n := flattenBinaryExprTree(tw, expr.X, lbl, 0) + flattenBinaryExprTree(tw, expr.Y, lbl, n) + } else { + tp := dbscheme.BinaryExprs[expr.Op] + if tp == nil { + log.Fatalf("unsupported binary operator %s", expr.Op) + } + kind = tp.Index() + extractExpr(tw, expr.X, lbl, 0) + extractExpr(tw, expr.Y, lbl, 1) } - kind = tp.Index() - extractExpr(tw, expr.X, lbl, 0) - extractExpr(tw, expr.Y, lbl, 1) case *ast.ArrayType: if expr == nil { return @@ -984,6 +995,9 @@ func extractExpr(tw *trap.Writer, expr ast.Expr, parent trap.Label, idx int) { return } kind = dbscheme.InterfaceTypeExpr.Index() + // expr.Methods contains methods, embedded interfaces and type set + // literals. + overrideTypesOfTypeSetLiterals(tw, expr.Methods) extractFields(tw, expr.Methods, lbl, 0, 1) case *ast.MapType: if expr == nil { @@ -1027,7 +1041,7 @@ func extractExprs(tw *trap.Writer, exprs []ast.Expr, parent trap.Label, idx int, // extractTypeOf looks up the type of `expr`, extracts it if it hasn't previously been // extracted, and associates it with `expr` in the `type_of` table func extractTypeOf(tw *trap.Writer, expr ast.Expr, lbl trap.Label) { - tp := tw.Package.TypesInfo.TypeOf(expr) + tp := typeOf(tw, expr) if tp != nil { tplbl := extractType(tw, tp) dbscheme.TypeOfTable.Emit(tw, lbl, tplbl) @@ -1421,6 +1435,13 @@ func extractType(tw *trap.Writer, tp types.Type) trap.Label { extractComponentType(tw, lbl, i, meth.Name(), meth.Type()) } + for i := 0; i < tp.NumEmbeddeds(); i++ { + component := tp.EmbeddedType(i) + if isNonUnionTypeSetLiteral(component) { + component = createUnionFromType(component) + } + extractComponentType(tw, lbl, -(i + 1), "", component) + } case *types.Tuple: kind = dbscheme.TupleType.Index() for i := 0; i < tp.Len(); i++ { @@ -1485,6 +1506,16 @@ func extractType(tw *trap.Writer, tp types.Type) trap.Label { case *types.TypeParam: kind = dbscheme.TypeParamType.Index() dbscheme.TypeParamTable.Emit(tw, lbl, tp.Obj().Name(), extractType(tw, tp.Constraint())) + case *types.Union: + kind = dbscheme.TypeSetLiteral.Index() + for i := 0; i < tp.Len(); i++ { + term := tp.Term(i) + tildeStr := "" + if term.Tilde() { + tildeStr = "~" + } + extractComponentType(tw, lbl, i, tildeStr, term.Type()) + } default: log.Fatalf("unexpected type %T", tp) } @@ -1544,6 +1575,19 @@ func getTypeLabel(tw *trap.Writer, tp types.Type) (trap.Label, bool) { } fmt.Fprintf(&b, "%s,{%s}", meth.Id(), methLbl) } + b.WriteString(";") + for i := 0; i < tp.NumEmbeddeds(); i++ { + if i > 0 { + b.WriteString(",") + } + fmt.Fprintf(&b, "{%s}", extractType(tw, tp.EmbeddedType(i))) + } + // We note whether the interface is comparable so that we can + // distinguish the underlying type of `comparable` from an + // empty interface. + if tp.IsComparable() { + b.WriteString(";comparable") + } lbl = tw.Labeler.GlobalID(fmt.Sprintf("%s;interfacetype", b.String())) case *types.Tuple: var b strings.Builder @@ -1600,8 +1644,20 @@ func getTypeLabel(tw *trap.Writer, tp types.Type) (trap.Label, bool) { } lbl = tw.Labeler.GlobalID(fmt.Sprintf("{%s};namedtype", entitylbl)) case *types.TypeParam: - constraint := extractType(tw, tp.Constraint()) - lbl = tw.Labeler.GlobalID(fmt.Sprintf("{%s},{%s};typeparamtype", tp.Obj().Name(), constraint)) + lbl = tw.Labeler.GlobalID(fmt.Sprintf("{%s},{%s};typeparamtype", tp.Obj().Name(), extractType(tw, tp.Constraint()))) + case *types.Union: + var b strings.Builder + for i := 0; i < tp.Len(); i++ { + compLbl := extractType(tw, tp.Term(i).Type()) + if i > 0 { + b.WriteString("|") + } + if tp.Term(i).Tilde() { + b.WriteString("~") + } + fmt.Fprintf(&b, "{%s}", compLbl) + } + lbl = tw.Labeler.GlobalID(fmt.Sprintf("%s;typesetliteraltype", b.String())) default: log.Fatalf("(getTypeLabel) unexpected type %T", tp) } @@ -1686,3 +1742,59 @@ func extractNumLines(tw *trap.Writer, fileName string, ast *ast.File) { dbscheme.NumlinesTable.Emit(tw, tw.Labeler.FileLabel(), lineCount, linesOfCode, linesOfComments) } + +// For a type `t` which is the type of a field of an interface type, return +// whether `t` a type set literal which is not a union type. Note that a field +// of an interface must be a method signature, an embedded interface type or a +// type set literal. +func isNonUnionTypeSetLiteral(t types.Type) bool { + switch t.Underlying().(type) { + case *types.Interface, *types.Union, *types.Signature: + return false + default: + return true + } +} + +// Given a type `t`, return a union with a single term that is `t` without a +// tilde. +func createUnionFromType(t types.Type) *types.Union { + return types.NewUnion([]*types.Term{types.NewTerm(false, t)}) +} + +// Go through a `FieldList` and update the types of all type set literals which +// are not already union types to be union types. We do this by changing the +// types stored in `tw.Package.TypesInfo.Types`. +func overrideTypesOfTypeSetLiterals(tw *trap.Writer, fields *ast.FieldList) { + if fields == nil || fields.List == nil { + return + } + for i := 0; i < len(fields.List); i++ { + x := fields.List[i].Type + if _, alreadyOverridden := tw.TypesOverride[x]; !alreadyOverridden { + xtp := typeOf(tw, x) + if isNonUnionTypeSetLiteral(xtp) { + tw.TypesOverride[x] = createUnionFromType(xtp) + } + } + } +} + +func typeOf(tw *trap.Writer, e ast.Expr) types.Type { + if val, ok := tw.TypesOverride[e]; ok { + return val + } + return tw.Package.TypesInfo.TypeOf(e) +} + +func flattenBinaryExprTree(tw *trap.Writer, e ast.Expr, parent trap.Label, idx int) int { + binaryexpr, ok := e.(*ast.BinaryExpr) + if ok { + idx = flattenBinaryExprTree(tw, binaryexpr.X, parent, idx) + idx = flattenBinaryExprTree(tw, binaryexpr.Y, parent, idx) + } else { + extractExpr(tw, e, parent, idx) + idx = idx + 1 + } + return idx +} diff --git a/extractor/trap/trapwriter.go b/extractor/trap/trapwriter.go index ff96defa3bd..299147e8a53 100644 --- a/extractor/trap/trapwriter.go +++ b/extractor/trap/trapwriter.go @@ -5,6 +5,7 @@ import ( "compress/gzip" "errors" "fmt" + "go/ast" "go/types" "io/ioutil" "os" @@ -17,13 +18,14 @@ import ( // A Writer provides methods for writing data to a TRAP file type Writer struct { - zip *gzip.Writer - w *bufio.Writer - file *os.File - Labeler *Labeler - path string - trapFilePath string - Package *packages.Package + zip *gzip.Writer + w *bufio.Writer + file *os.File + Labeler *Labeler + path string + trapFilePath string + Package *packages.Package + TypesOverride map[ast.Expr]types.Type } func FileFor(path string) (string, error) { @@ -61,6 +63,7 @@ func NewWriter(path string, pkg *packages.Package) (*Writer, error) { path, trapFilePath, pkg, + make(map[ast.Expr]types.Type), } tw.Labeler = newLabeler(tw) return tw, nil diff --git a/ql/lib/go.dbscheme b/ql/lib/go.dbscheme index ba44b5feecc..4c567b898b8 100644 --- a/ql/lib/go.dbscheme +++ b/ql/lib/go.dbscheme @@ -284,36 +284,37 @@ case @expr.kind of | 22 = @functypeexpr | 23 = @interfacetypeexpr | 24 = @maptypeexpr -| 25 = @plusexpr -| 26 = @minusexpr -| 27 = @notexpr -| 28 = @complementexpr -| 29 = @derefexpr -| 30 = @addressexpr -| 31 = @arrowexpr -| 32 = @lorexpr -| 33 = @landexpr -| 34 = @eqlexpr -| 35 = @neqexpr -| 36 = @lssexpr -| 37 = @leqexpr -| 38 = @gtrexpr -| 39 = @geqexpr -| 40 = @addexpr -| 41 = @subexpr -| 42 = @orexpr -| 43 = @xorexpr -| 44 = @mulexpr -| 45 = @quoexpr -| 46 = @remexpr -| 47 = @shlexpr -| 48 = @shrexpr -| 49 = @andexpr -| 50 = @andnotexpr -| 51 = @sendchantypeexpr -| 52 = @recvchantypeexpr -| 53 = @sendrcvchantypeexpr -| 54 = @errorexpr; +| 25 = @typesetliteralexpr +| 26 = @plusexpr +| 27 = @minusexpr +| 28 = @notexpr +| 29 = @complementexpr +| 30 = @derefexpr +| 31 = @addressexpr +| 32 = @arrowexpr +| 33 = @lorexpr +| 34 = @landexpr +| 35 = @eqlexpr +| 36 = @neqexpr +| 37 = @lssexpr +| 38 = @leqexpr +| 39 = @gtrexpr +| 40 = @geqexpr +| 41 = @addexpr +| 42 = @subexpr +| 43 = @orexpr +| 44 = @xorexpr +| 45 = @mulexpr +| 46 = @quoexpr +| 47 = @remexpr +| 48 = @shlexpr +| 49 = @shrexpr +| 50 = @andexpr +| 51 = @andnotexpr +| 52 = @sendchantypeexpr +| 53 = @recvchantypeexpr +| 54 = @sendrcvchantypeexpr +| 55 = @errorexpr; @basiclit = @intlit | @floatlit | @imaglit | @charlit | @stringlit; @@ -492,7 +493,8 @@ case @type.kind of | 35 = @sendchantype | 36 = @recvchantype | 37 = @sendrcvchantype -| 38 = @namedtype; +| 38 = @namedtype +| 39 = @typesetliteraltype; @basictype = @booltype | @numerictype | @stringtype | @literaltype | @invalidtype | @unsafepointertype; @@ -516,7 +518,7 @@ case @type.kind of | @stringliteraltype | @nilliteraltype; @compositetype = @typeparamtype | @containertype | @structtype | @pointertype | @interfacetype | @tupletype - | @signaturetype | @namedtype; + | @signaturetype | @namedtype | @typesetliteraltype; @containertype = @arraytype | @slicetype | @maptype | @chantype; diff --git a/ql/lib/semmle/go/Expr.qll b/ql/lib/semmle/go/Expr.qll index db3956693e3..9b9d0617460 100644 --- a/ql/lib/semmle/go/Expr.qll +++ b/ql/lib/semmle/go/Expr.qll @@ -1069,6 +1069,22 @@ class MapTypeExpr extends @maptypeexpr, TypeExpr { override string getAPrimaryQlClass() { result = "MapTypeExpr" } } +/** + * An expression representing a type set literal. + * + * Examples: + * + * ```go + * ~string + * int64 | float64 + * ``` + */ +class TypeSetLiteralExpr extends @typesetliteralexpr, TypeExpr { + override string toString() { result = "type set literal" } + + override string getAPrimaryQlClass() { result = "TypeSetLiteralExpr" } +} + /** * An expression with a (unary or binary) operator. * @@ -2044,6 +2060,8 @@ private predicate isTypeExprBottomUp(Expr e) { or e instanceof @chantypeexpr or + e instanceof @typesetliteralexpr + or e instanceof @genericfunctioninstantiationexpr or e instanceof @generictypeinstantiationexpr diff --git a/ql/lib/semmle/go/Types.qll b/ql/lib/semmle/go/Types.qll index 5d3df3f35af..21ad88e83d7 100644 --- a/ql/lib/semmle/go/Types.qll +++ b/ql/lib/semmle/go/Types.qll @@ -66,18 +66,76 @@ class Type extends @type { /** * Holds if this type implements interface `i`, that is, the method set of `i` - * is contained in the method set of this type. + * is contained in the method set of this type and any type restrictions are + * satisfied. */ predicate implements(InterfaceType i) { - isEmptyInterface(i) - or - this.hasMethod(getExampleMethodName(i), _) and - forall(string m, SignatureType t | i.hasMethod(m, t) | this.hasMethod(m, t)) + if i = any(ComparableType comparable).getUnderlyingType() + then this.implementsComparable() + else implementsNotComparable(i) + } + + /** + * Holds if this type implements interface `i`, which is not the underlying + * type of `comparable`. This predicate is needed to avoid non-monotonic + * recursion. + */ + private predicate implementsNotComparable(InterfaceType i) { + ( + forall(TypeSetLiteralType tslit | tslit = i.getAnEmbeddedTypeSetLiteral() | + tslit.hasInTypeSet(this) + ) and + ( + not i.hasMethod(_, _) + or + this.hasMethod(getExampleMethodName(i), _) and + forall(string m, SignatureType t | i.hasMethod(m, t) | this.hasMethod(m, t)) + ) + ) + } + + /** + * Holds if this type implements `comparable`. This includes being + * `comparable` itself, or the underlying type of `comparable`. + */ + predicate implementsComparable() { + exists(Type u | u = this.getUnderlyingType() | + // Note that BasicType includes Invalidtype + u instanceof BasicType + or + u instanceof PointerType + or + u instanceof ChanType + or + u instanceof StructType and + forall(Type fieldtp | u.(StructType).hasField(_, fieldtp) | fieldtp.implementsComparable()) + or + u instanceof ArrayType and u.(ArrayType).getElementType().implementsComparable() + or + u instanceof InterfaceType and + ( + not u instanceof BasicInterfaceType and + if exists(u.(InterfaceType).getAnEmbeddedTypeSetLiteral()) + then + forall(Type intersectionType | + intersectionType = u.(InterfaceType).getAnEmbeddedTypeSetLiteral().getATerm().getType() and + forall(TypeSetLiteralType tslit | + tslit = u.(InterfaceType).getAnEmbeddedTypeSetLiteral() + | + intersectionType = tslit.getATerm().getType() + ) + | + intersectionType.implementsComparable() + ) + else u.(InterfaceType).isOrEmbedsComparable() + ) + ) } /** * Holds if this type implements an interface that has the qualified name `pkg.name`, - * that is, the method set of `pkg.name` is contained in the method set of this type. + * that is, the method set of `pkg.name` is contained in the method set of this type + * and any type restrictions are satisfied. */ predicate implements(string pkg, string name) { exists(Type t | t.hasQualifiedName(pkg, name) | this.implements(t.getUnderlyingType())) @@ -552,35 +610,260 @@ class PointerType extends @pointertype, CompositeType { override string toString() { result = "pointer type" } } -/** An interface type. */ -class InterfaceType extends @interfacetype, CompositeType { - /** Gets the type of method `name` of this interface type. */ - Type getMethodType(string name) { component_types(this, _, name, result) } +private newtype TTerm = + MkTerm(TypeSetLiteralType tslit, int index) { component_types(tslit, index, _, _) } - override predicate hasMethod(string m, SignatureType t) { t = this.getMethodType(m) } +/** + * A term in a type set literal. + * + * Examples: + * ```go + * int + * ~string + * ``` + */ +class Term extends TTerm { + boolean tilde; + Type tp; + + Term() { + exists(TypeSetLiteralType tslit, int index | + this = MkTerm(tslit, index) and + ( + component_types(tslit, index, "", tp) and + tilde = false + or + component_types(tslit, index, "~", tp) and + tilde = true + ) + ) + } + + /** + * Holds if this term has a tilde in front of it. + * + * A tilde is used to indicate that the term refers to all types with a given + * underlying type. + */ + predicate hasTilde() { tilde = true } + + /** Gets the type of this term. */ + Type getType() { result = tp } + + /** Holds if `t` is in the type set of this term. */ + predicate hasInTypeSet(Type t) { if tilde = false then t = tp else t.getUnderlyingType() = tp } + + /** Gets a pretty-printed representation of this term. */ + string pp() { + exists(string tildeStr | if tilde = true then tildeStr = "~" else tildeStr = "" | + result = tildeStr + tp.pp() + ) + } + + /** Gets a textual representation of this element. */ + string toString() { result = "term" } +} + +private Term getIntersection(Term term1, Term term2) { + term1.getType() = term2.getType() and + if term1.hasTilde() then result = term2 else result = term1 +} + +Term getTermInIntersection(TypeSetLiteralType a, TypeSetLiteralType b) { + result = getIntersection(a.getATerm(), b.getATerm()) +} + +/** + * A type set literal type, used when declaring a non-basic interface. May be a + * single term, consisting of either a type or a tilde followed by a type, or a + * union of terms. + * + * + * Examples: + * + * ```go + * int + * ~string + * int | ~string + * ``` + */ +class TypeSetLiteralType extends @typesetliteraltype, CompositeType { + /** Gets the `i`th term in this type set literal. */ + Term getTerm(int i) { result = MkTerm(this, i) } + + /** Gets a term in this type set literal. */ + Term getATerm() { result = getTerm(_) } + + /** Holds if `t` is in the type set of this type set literal. */ + predicate hasInTypeSet(Type t) { exists(int i | this.getTerm(i).hasInTypeSet(t)) } + + /** + * Gets the interface type specified by just this type set literal, if it + * exists. + * + * This will exist in cases where the type set literal is used directly as + * the bound in a type parameter declaration. + */ + InterfaceType getInterfaceType() { + this = result.getExplicitlyEmbeddedTypeSetLiteral(0) and + not exists(result.getExplicitlyEmbeddedTypeSetLiteral(1)) and + not result.hasMethod(_, _) and + not exists(result.getAnExplicitlyEmbeddedInterface()) + } language[monotonicAggregates] override string pp() { - exists(string meth | + result = concat(Term t, int i | t = this.getTerm(i) | t.pp(), " | " order by i) + } + + override string toString() { result = "type set literal type" } +} + +/** An interface type. */ +class InterfaceType extends @interfacetype, CompositeType { + /** Gets the type of method `name` of this interface type. */ + Type getMethodType(string name) { + exists(int i | i >= 0 | component_types(this, i, name, result)) + } + + override predicate hasMethod(string m, SignatureType t) { t = this.getMethodType(m) } + + /** + * Holds if `tp` is an explicitly embedded type with index `index`. + * + * `tp` is either a type set literal type or its underlyign type is an + * interface type. + */ + private predicate hasExplicitlyEmbeddedType(int index, Type tp) { + index >= 0 and component_types(this, -(index + 1), _, tp) + } + + /** + * Gets a type whose underlying type is an interface that is explicitly + * embedded into this interface. + * + * Note that the methods of the embedded interface are already considered + * as part of the method set of this interface. + */ + Type getAnExplicitlyEmbeddedInterface() { + hasExplicitlyEmbeddedType(_, result) and result.getUnderlyingType() instanceof InterfaceType + } + + /** + * Gets a type whose underlying type is an interface that is embedded into + * this interface. + * + * Note that the methods of the embedded interface are already considered + * as part of the method set of this interface. + */ + Type getAnEmbeddedInterface() { + result = this.getAnExplicitlyEmbeddedInterface() or + result = + this.getAnExplicitlyEmbeddedInterface() + .getUnderlyingType() + .(InterfaceType) + .getAnEmbeddedInterface() + } + + /** + * Holds if this interface type is (the underlying type of) `comparable`, or + * it embeds `comparable`. + */ + predicate isOrEmbedsComparable() { + this.getAnEmbeddedInterface() instanceof ComparableType or + this = any(ComparableType comparable).getUnderlyingType() + } + + /** + * Gets the type set literal with index `index` from the definition of this + * interface type. + * + * Note that the indexing includes embedded interfaces but not methods. + */ + TypeSetLiteralType getExplicitlyEmbeddedTypeSetLiteral(int index) { + hasExplicitlyEmbeddedType(index, result) + } + + /** + * Gets a type set literal of this interface type. + * + * This includes type set literals of embedded interfaces. + */ + TypeSetLiteralType getAnEmbeddedTypeSetLiteral() { + result = this.getExplicitlyEmbeddedTypeSetLiteral(_) or + result = + getAnExplicitlyEmbeddedInterface() + .getUnderlyingType() + .(InterfaceType) + .getAnEmbeddedTypeSetLiteral() + } + + language[monotonicAggregates] + override string pp() { + exists(string comp, string sep1, string ts, string sep2, string meth | + // Note that the interface type underlying `comparable` will be printed + // as `interface { comparable }`, which is not entirely accurate, but + // also better than anything else I can think of. + (if this.isOrEmbedsComparable() then comp = " comparable" else comp = "") and + ts = + concat(TypeSetLiteralType tslit | + tslit = this.getAnEmbeddedTypeSetLiteral() + | + " " + tslit.pp(), ";" + ) and meth = concat(string name, Type tp | tp = this.getMethodType(name) | " " + name + " " + tp.pp(), ";" order by name - ) + ) and + (if comp != "" and ts != "" then sep1 = ";" else sep1 = "") and + if + (comp != "" or ts != "") and + meth != "" + then sep2 = ";" + else sep2 = "" | - result = "interface {" + meth + " }" + result = "interface {" + comp + sep1 + ts + sep2 + meth + " }" ) } override string toString() { result = "interface type" } } -/** An empty interface type. */ -class EmptyInterfaceType extends InterfaceType { +/** + * A basic interface type. + * + * A basic interface is an interface that does not specify any type set + * literals, and which does not embed any non-basic interfaces. The special + * interface `comparable` is not a basic interface. + */ +class BasicInterfaceType extends InterfaceType { + BasicInterfaceType() { + not exists(this.getAnEmbeddedTypeSetLiteral()) and + not this.isOrEmbedsComparable() + } + + override string toString() { result = "basic interface type" } +} + +/** + * An empty interface type. + * + * Note that by we have to be careful to exclude the underlying type of + * `comparable`. This is done by extending `BasicInterfaceType`. + */ +class EmptyInterfaceType extends BasicInterfaceType { EmptyInterfaceType() { not this.hasMethod(_, _) } } +/** + * The predeclared `comparable` type. + */ +class ComparableType extends NamedType { + ComparableType() { this.getName() = "comparable" } +} + /** A tuple type. */ class TupleType extends @tupletype, CompositeType { /** Gets the `i`th component type of this tuple type. */ @@ -713,12 +996,6 @@ class ErrorType extends Type { ErrorType() { this.implements(Builtin::error().getType().getUnderlyingType()) } } -/** - * Holds if `i` is the empty interface type, which is implemented by every type with a method set. - */ -pragma[noinline] -private predicate isEmptyInterface(InterfaceType i) { not i.hasMethod(_, _) } - /** * Gets the name of a method in the method set of `i`. * From 1e082016321db94c279dda2e2a9f84eb76231b35 Mon Sep 17 00:00:00 2001 From: Owen Mansel-Chan Date: Tue, 29 Mar 2022 12:30:29 +0100 Subject: [PATCH 08/49] Extract type param declarations --- extractor/dbscheme/tables.go | 17 +++++++++-- extractor/extractor.go | 30 ++++++++++++++++++++ ql/lib/go.dbscheme | 12 +++++--- ql/lib/semmle/go/AST.qll | 18 ++++++++++++ ql/lib/semmle/go/Decls.qll | 55 ++++++++++++++++++++++++++++++++++-- ql/lib/semmle/go/Expr.qll | 4 +++ 6 files changed, 128 insertions(+), 8 deletions(-) diff --git a/extractor/dbscheme/tables.go b/extractor/dbscheme/tables.go index 6bf253a103f..6325c6fd2a0 100644 --- a/extractor/dbscheme/tables.go +++ b/extractor/dbscheme/tables.go @@ -275,6 +275,9 @@ var StmtParentType = NewUnionType("@stmtparent", NodeType) // DeclParentType is the type of AST nodes that can have declarations as children var DeclParentType = NewUnionType("@declparent", NodeType) +// TypeParamDeclParentType is the type of AST nodes that can have type parameter declarations as children +var TypeParamDeclParentType = NewUnionType("@typeparamdeclparent", NodeType) + // FuncDefType is the type of AST nodes that define functions, that is, function // declarations and function literals var FuncDefType = NewUnionType("@funcdef", StmtParentType, ExprParentType) @@ -309,6 +312,9 @@ var StmtType = NewPrimaryKeyType("@stmt", ExprParentType, StmtParentType) // DeclType is the type of declaration AST nodes var DeclType = NewPrimaryKeyType("@decl", ExprParentType, StmtParentType, FieldParentType) +// TypeParamDeclType is the type of type parameter declaration AST nodes +var TypeParamDeclType = NewPrimaryKeyType("@typeparamdecl", DocumentableType, ExprParentType) + // SpecType is the type of spec AST nodes var SpecType = NewPrimaryKeyType("@spec", ExprParentType, DocumentableType) @@ -650,7 +656,7 @@ var TypeDeclType = DeclKind.NewBranch("@typedecl", GenDeclType) var VarDeclType = DeclKind.NewBranch("@vardecl", GenDeclType) // FuncDeclType is the type of function declaration AST nodes -var FuncDeclType = DeclKind.NewBranch("@funcdecl", DocumentableType, FuncDefType) +var FuncDeclType = DeclKind.NewBranch("@funcdecl", DocumentableType, FuncDefType, TypeParamDeclParentType) // SpecKind is a case type for distinguishing different kinds of declaration specification nodes var SpecKind = NewCaseType(SpecType, "kind") @@ -662,7 +668,7 @@ var ImportSpecType = SpecKind.NewBranch("@importspec") var ValueSpecType = SpecKind.NewBranch("@valuespec") // TypeSpecType is the type of type declaration specification nodes -var TypeSpecType = NewUnionType("@typespec") +var TypeSpecType = NewUnionType("@typespec", TypeParamDeclParentType) // TypeDefSpecType is the type of type declaration specification nodes corresponding to type definitions var TypeDefSpecType = SpecKind.NewBranch("@typedefspec", TypeSpecType) @@ -991,6 +997,13 @@ var FieldsTable = NewTable("fields", IntColumn("idx"), ) +// TypeParamDeclsTable is the table defining type param declaration AST nodes +var TypeParamDeclsTable = NewTable("typeparamdecls", + EntityColumn(TypeParamDeclType, "id").Key(), + EntityColumn(TypeParamDeclParentType, "parent"), + IntColumn("idx"), +) + // StmtsTable is the table defining statement AST nodes var StmtsTable = NewTable("stmts", EntityColumn(StmtType, "id").Key(), diff --git a/extractor/extractor.go b/extractor/extractor.go index 29a272515b3..c5407d4cf7e 100644 --- a/extractor/extractor.go +++ b/extractor/extractor.go @@ -1336,6 +1336,12 @@ func extractDecl(tw *trap.Writer, decl ast.Decl, parent trap.Label, idx int) { extractExpr(tw, decl.Type, lbl, 1) extractStmt(tw, decl.Body, lbl, 2) extractDoc(tw, decl.Doc, lbl) + extractTypeParamDecls(tw, decl.Type.TypeParams, lbl) + + // Note that we currently don't extract any kind of declaration for + // receiver type parameters. There isn't an explicit declaration, but + // we could consider the index/indices of an IndexExpr/IndexListExpr + // receiver as declarations. default: log.Fatalf("unknown declaration of type %T", decl) } @@ -1377,6 +1383,7 @@ func extractSpec(tw *trap.Writer, spec ast.Spec, parent trap.Label, idx int) { kind = dbscheme.TypeDefSpecType.Index() } extractExpr(tw, spec.Name, lbl, 0) + extractTypeParamDecls(tw, spec.TypeParams, lbl) extractExpr(tw, spec.Type, lbl, 1) extractDoc(tw, spec.Doc, lbl) } @@ -1798,3 +1805,26 @@ func flattenBinaryExprTree(tw *trap.Writer, e ast.Expr, parent trap.Label, idx i } return idx } + +func extractTypeParamDecls(tw *trap.Writer, fields *ast.FieldList, parent trap.Label) { + if fields == nil || fields.List == nil { + return + } + + overrideTypesOfTypeSetLiterals(tw, fields) + + idx := 0 + for _, field := range fields.List { + lbl := tw.Labeler.LocalID(field) + dbscheme.TypeParamDeclsTable.Emit(tw, lbl, parent, idx) + extractNodeLocation(tw, field, lbl) + if field.Names != nil { + for i, name := range field.Names { + extractExpr(tw, name, lbl, i+1) + } + } + extractExpr(tw, field.Type, lbl, 0) + extractDoc(tw, field.Doc, lbl) + idx += 1 + } +} diff --git a/ql/lib/go.dbscheme b/ql/lib/go.dbscheme index 4c567b898b8..3110ca88d98 100644 --- a/ql/lib/go.dbscheme +++ b/ql/lib/go.dbscheme @@ -157,6 +157,8 @@ constvalues(unique int expr: @expr ref, string value: string ref, string exact: fields(unique int id: @field, int parent: @fieldparent ref, int idx: int ref); +typeparamdecls(unique int id: @typeparamdecl, int parent: @typeparamdeclparent ref, int idx: int ref); + #keyset[parent, idx] stmts(unique int id: @stmt, int kind: int ref, int parent: @stmtparent ref, int idx: int ref); @@ -231,12 +233,12 @@ typeparam(unique int tp: @typeparamtype ref, string name: string ref, int bound: @locatable = @xmllocatable | @node | @localscope; -@node = @documentable | @exprparent | @modexprparent | @fieldparent | @stmtparent | @declparent | @scopenode - | @comment_group | @comment; +@node = @documentable | @exprparent | @modexprparent | @fieldparent | @stmtparent | @declparent | @typeparamdeclparent + | @scopenode | @comment_group | @comment; -@documentable = @file | @field | @spec | @gendecl | @funcdecl | @modexpr; +@documentable = @file | @field | @typeparamdecl | @spec | @gendecl | @funcdecl | @modexpr; -@exprparent = @funcdef | @file | @expr | @field | @stmt | @decl | @spec; +@exprparent = @funcdef | @file | @expr | @field | @stmt | @decl | @typeparamdecl | @spec; @modexprparent = @file | @modexpr; @@ -246,6 +248,8 @@ typeparam(unique int tp: @typeparamtype ref, string name: string ref, int bound: @declparent = @file | @declstmt; +@typeparamdeclparent = @functypeexpr | @typespec; + @funcdef = @funclit | @funcdecl; @scopenode = @file | @functypeexpr | @blockstmt | @ifstmt | @caseclause | @switchstmt | @commclause | @loopstmt; diff --git a/ql/lib/semmle/go/AST.qll b/ql/lib/semmle/go/AST.qll index 6ce205aefae..6c632af5e9b 100644 --- a/ql/lib/semmle/go/AST.qll +++ b/ql/lib/semmle/go/AST.qll @@ -216,6 +216,24 @@ class FieldParent extends @fieldparent, AstNode { int getNumFields() { result = count(getAField()) } } +/** + * An AST node whose children include type parameter declarations. + */ +class TypeParamDeclParent extends @typeparamdeclparent, AstNode { + /** + * Gets the `i`th type parameter declaration of this node. + * + * Note that the precise indices of type parameters are considered an implementation detail + * and are subject to change without notice. + */ + TypeParamDecl getTypeParameterDecl(int i) { typeparamdecls(result, this, i) } + + /** + * Gets a child field of this node in the AST. + */ + TypeParamDecl getATypeParameterDecl() { result = getTypeParameterDecl(_) } +} + /** * An AST node which may induce a scope. * diff --git a/ql/lib/semmle/go/Decls.qll b/ql/lib/semmle/go/Decls.qll index 3e3d5d1b831..0623aa1cc45 100644 --- a/ql/lib/semmle/go/Decls.qll +++ b/ql/lib/semmle/go/Decls.qll @@ -146,7 +146,7 @@ class FuncDef extends @funcdef, StmtParent, ExprParent { /** * A function declaration. */ -class FuncDecl extends @funcdecl, Decl, Documentable, FuncDef { +class FuncDecl extends @funcdecl, Decl, Documentable, FuncDef, TypeParamDeclParent { /** Gets the identifier denoting the name of this function. */ Ident getNameExpr() { result = this.getChildExpr(0) } @@ -376,7 +376,7 @@ class ValueSpec extends @valuespec, Spec { * ) * ``` */ -class TypeSpec extends @typespec, Spec { +class TypeSpec extends @typespec, Spec, TypeParamDeclParent { /** Gets the identifier denoting the name of the declared type. */ Ident getNameExpr() { result = this.getChildExpr(0) } @@ -562,6 +562,57 @@ class ResultVariableDecl extends ParameterOrResultDecl { override string getAPrimaryQlClass() { result = "ResultVariableDecl" } } +/** + * A type parameter declaration in a type specification. + */ +class TypeParamDecl extends @typeparamdecl, Documentable, ExprParent { + TypeParamDecl() { typeparamdecls(this, _, _) } + + /** + * Gets the expression representing the type constraint of the type + * parameters in this declaration. + * + * If you want the type constraint type itself then use `getTypeConstraint`, + * as that wraps type set literals with implicit interface types. + */ + Expr getTypeConstraintExpr() { result = this.getChildExpr(0) } + + /** + * Gets the type constraint of the type parameters in this declaration. + * + * If the type constraint is a type set literal then it will be wrapped + * with an implicit interface type. + */ + Type getTypeConstraint() { + exists(Type t | t = this.getTypeConstraintExpr().getType() | + if t instanceof TypeSetLiteralType + then result = t.(TypeSetLiteralType).getInterfaceType() + else result = t + ) + } + + /** + * Gets the expression representing the name of the `i`th type parameter + * in this declaration (0-based). + */ + Expr getNameExpr(int i) { + i >= 0 and + result = this.getChildExpr(i + 1) + } + + /** + * Gets the `i`th type parameter type in this declaration (0-based). + */ + TypeParamType getTypeParamType(int i) { + i >= 0 and + result = this.getNameExpr(i).getType() + } + + override string toString() { result = "type parameter declaration" } + + override string getAPrimaryQlClass() { result = "TypeParamDecl" } +} + /** * A method or embedding specification in an interface type expression. */ diff --git a/ql/lib/semmle/go/Expr.qll b/ql/lib/semmle/go/Expr.qll index 9b9d0617460..6268885f494 100644 --- a/ql/lib/semmle/go/Expr.qll +++ b/ql/lib/semmle/go/Expr.qll @@ -2096,6 +2096,10 @@ private predicate isTypeExprTopDown(Expr e) { or e = any(ParameterDecl pd).getTypeExpr() or + e = any(TypeParamDecl tpd).getTypeConstraintExpr() + or + e = any(TypeParamDecl tpd).getNameExpr(_) + or e = any(ReceiverDecl rd).getTypeExpr() or e = any(ResultVariableDecl rvd).getTypeExpr() From f908a6f1dc68986b757a78208ed1d7543ff0da72 Mon Sep 17 00:00:00 2001 From: Owen Mansel-Chan Date: Thu, 17 Mar 2022 07:31:55 +0000 Subject: [PATCH 09/49] Rename `parm` to `param` for clarity --- extractor/extractor.go | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/extractor/extractor.go b/extractor/extractor.go index c5407d4cf7e..b8856448298 100644 --- a/extractor/extractor.go +++ b/extractor/extractor.go @@ -1456,11 +1456,11 @@ func extractType(tw *trap.Writer, tp types.Type) trap.Label { } case *types.Signature: kind = dbscheme.SignatureType.Index() - parms, results := tp.Params(), tp.Results() - if parms != nil { - for i := 0; i < parms.Len(); i++ { - parm := parms.At(i) - extractComponentType(tw, lbl, i+1, "", parm.Type()) + params, results := tp.Params(), tp.Results() + if params != nil { + for i := 0; i < params.Len(); i++ { + param := params.At(i) + extractComponentType(tw, lbl, i+1, "", param.Type()) } } if results != nil { @@ -1608,14 +1608,14 @@ func getTypeLabel(tw *trap.Writer, tp types.Type) (trap.Label, bool) { lbl = tw.Labeler.GlobalID(fmt.Sprintf("%s;tupletype", b.String())) case *types.Signature: var b strings.Builder - parms, results := tp.Params(), tp.Results() - if parms != nil { - for i := 0; i < parms.Len(); i++ { - parmLbl := extractType(tw, parms.At(i).Type()) + params, results := tp.Params(), tp.Results() + if params != nil { + for i := 0; i < params.Len(); i++ { + paramLbl := extractType(tw, params.At(i).Type()) if i > 0 { b.WriteString(",") } - fmt.Fprintf(&b, "{%s}", parmLbl) + fmt.Fprintf(&b, "{%s}", paramLbl) } } b.WriteString(";") From 982f11f8c71c4b3c0c8d4ea590b241eef383164b Mon Sep 17 00:00:00 2001 From: Owen Mansel-Chan Date: Thu, 31 Mar 2022 11:39:07 +0100 Subject: [PATCH 10/49] Make ScopedObjectID take a function This is so that ExtractType() won't be called except in the case of a receiver variable, which is important for extracting type parameters. --- extractor/extractor.go | 2 +- extractor/trap/labels.go | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/extractor/extractor.go b/extractor/extractor.go index b8856448298..5a207ad5fba 100644 --- a/extractor/extractor.go +++ b/extractor/extractor.go @@ -361,7 +361,7 @@ func extractUniverseScope() { func extractObjects(tw *trap.Writer, scope *types.Scope, scopeLabel trap.Label) { for _, name := range scope.Names() { obj := scope.Lookup(name) - lbl, exists := tw.Labeler.ScopedObjectID(obj, extractType(tw, obj.Type())) + lbl, exists := tw.Labeler.ScopedObjectID(obj, func() trap.Label { return extractType(tw, obj.Type()) }) if !exists { extractObject(tw, obj, lbl) } diff --git a/extractor/trap/labels.go b/extractor/trap/labels.go index 2176cdf929c..3747266ddd2 100644 --- a/extractor/trap/labels.go +++ b/extractor/trap/labels.go @@ -129,7 +129,7 @@ func (l *Labeler) LookupObjectID(object types.Object, typelbl Label) (Label, boo } label = InvalidLabel } else { - label, exists = l.ScopedObjectID(object, typelbl) + label, exists = l.ScopedObjectID(object, func() Label { return typelbl }) } } return label, exists @@ -144,7 +144,7 @@ func (l *Labeler) LookupObjectID(object types.Object, typelbl Label) (Label, boo // detected, we must construct a special label, as the variable can be reached // from several files via the method. As the type label is required to construct // the receiver object id, it is also required here. -func (l *Labeler) ScopedObjectID(object types.Object, typelbl Label) (Label, bool) { +func (l *Labeler) ScopedObjectID(object types.Object, getTypeLabel func() Label) (Label, bool) { label, exists := l.objectLabels[object] if !exists { scope := object.Parent() @@ -160,7 +160,7 @@ func (l *Labeler) ScopedObjectID(object types.Object, typelbl Label) (Label, boo meth := namedType.Method(i) if object == meth.Type().(*types.Signature).Recv() { isRecv = true - methlbl, _ := l.MethodID(meth, typelbl) + methlbl, _ := l.MethodID(meth, getTypeLabel()) label, _ = l.ReceiverObjectID(object, methlbl) } } From 213fa1fec2000f0c1eea87a5838aeab58525ee9a Mon Sep 17 00:00:00 2001 From: Owen Mansel-Chan Date: Thu, 31 Mar 2022 11:39:55 +0100 Subject: [PATCH 11/49] Break out of loop when a match has been found --- extractor/trap/labels.go | 1 + 1 file changed, 1 insertion(+) diff --git a/extractor/trap/labels.go b/extractor/trap/labels.go index 3747266ddd2..b42c8e54721 100644 --- a/extractor/trap/labels.go +++ b/extractor/trap/labels.go @@ -162,6 +162,7 @@ func (l *Labeler) ScopedObjectID(object types.Object, getTypeLabel func() Label) isRecv = true methlbl, _ := l.MethodID(meth, getTypeLabel()) label, _ = l.ReceiverObjectID(object, methlbl) + break } } } From 92c331402debfeff232d1c4479ed592668fbd7e2 Mon Sep 17 00:00:00 2001 From: Owen Mansel-Chan Date: Tue, 29 Mar 2022 14:54:49 +0100 Subject: [PATCH 12/49] Extract type parameters in types, not just decls --- extractor/dbscheme/tables.go | 12 +++++++++--- extractor/extractor.go | 34 +++++++++++++++++++++++++++++++--- extractor/trap/trapwriter.go | 18 ++++++++++-------- ql/lib/go.dbscheme | 8 ++++++-- ql/lib/semmle/go/Types.qll | 4 ++-- 5 files changed, 58 insertions(+), 18 deletions(-) diff --git a/extractor/dbscheme/tables.go b/extractor/dbscheme/tables.go index 6325c6fd2a0..00b3bef14b3 100644 --- a/extractor/dbscheme/tables.go +++ b/extractor/dbscheme/tables.go @@ -682,6 +682,9 @@ var ObjectType = NewPrimaryKeyType("@object") // ObjectKind is a case type for distinguishing different kinds of built-in and declared objects var ObjectKind = NewCaseType(ObjectType, "kind") +// TypeParamParentObjectType is the type of objects that can have type parameters as children +var TypeParamParentObjectType = NewUnionType("@typeparamparentobject") + // DeclObjectType is the type of declared objects var DeclObjectType = NewUnionType("@declobject") @@ -695,7 +698,7 @@ var PkgObjectType = ObjectKind.NewBranch("@pkgobject") var TypeObjectType = NewUnionType("@typeobject") // DeclTypeObjectType is the type of declared named types -var DeclTypeObjectType = ObjectKind.NewBranch("@decltypeobject", TypeObjectType, DeclObjectType) +var DeclTypeObjectType = ObjectKind.NewBranch("@decltypeobject", TypeObjectType, DeclObjectType, TypeParamParentObjectType) // BuiltinTypeObjectType is the type of built-in named types var BuiltinTypeObjectType = ObjectKind.NewBranch("@builtintypeobject", TypeObjectType, BuiltinObjectType) @@ -722,7 +725,7 @@ var DeclVarObjectType = ObjectKind.NewBranch("@declvarobject", VarObjectType, De var FunctionObjectType = NewUnionType("@functionobject", ValueObjectType) // DeclFuncObjectType is the type of declared functions, including (abstract and concrete) methods -var DeclFuncObjectType = ObjectKind.NewBranch("@declfunctionobject", FunctionObjectType, DeclObjectType) +var DeclFuncObjectType = ObjectKind.NewBranch("@declfunctionobject", FunctionObjectType, DeclObjectType, TypeParamParentObjectType) // BuiltinFuncObjectType is the type of built-in functions var BuiltinFuncObjectType = ObjectKind.NewBranch("@builtinfunctionobject", FunctionObjectType, BuiltinObjectType) @@ -1206,8 +1209,11 @@ var VariadicTable = NewTable("variadic", EntityColumn(SignatureType, "id"), ) +// TypeParamTable is the table describing type parameter types var TypeParamTable = NewTable("typeparam", EntityColumn(TypeParamType, "tp").Unique(), StringColumn("name"), EntityColumn(CompositeType, "bound"), -) + EntityColumn(TypeParamParentObjectType, "parent"), + IntColumn("idx"), +).KeySet("parent", "idx") diff --git a/extractor/extractor.go b/extractor/extractor.go index 5a207ad5fba..e8e15ed32c2 100644 --- a/extractor/extractor.go +++ b/extractor/extractor.go @@ -363,6 +363,15 @@ func extractObjects(tw *trap.Writer, scope *types.Scope, scopeLabel trap.Label) obj := scope.Lookup(name) lbl, exists := tw.Labeler.ScopedObjectID(obj, func() trap.Label { return extractType(tw, obj.Type()) }) if !exists { + if funcObj, ok := obj.(*types.Func); ok { + populateTypeParamParents(tw, funcObj.Type().(*types.Signature).TypeParams(), lbl) + populateTypeParamParents(tw, funcObj.Type().(*types.Signature).RecvTypeParams(), lbl) + } + if typeNameObj, ok := obj.(*types.TypeName); ok { + if tp, ok := typeNameObj.Type().(*types.Named); ok { + populateTypeParamParents(tw, tp.TypeParams(), lbl) + } + } extractObject(tw, obj, lbl) } @@ -378,10 +387,14 @@ func extractObjects(tw *trap.Writer, scope *types.Scope, scopeLabel trap.Label) func extractMethod(tw *trap.Writer, meth *types.Func) trap.Label { // get the receiver type of the method recvtyp := meth.Type().(*types.Signature).Recv().Type() - recvlbl := extractType(tw, recvtyp) // ensure receiver type has been extracted + // ensure receiver type has been extracted + recvlbl := extractType(tw, recvtyp) + // if the method label does not exist, extract it methlbl, exists := tw.Labeler.MethodID(meth, recvlbl) if !exists { + populateTypeParamParents(tw, meth.Type().(*types.Signature).TypeParams(), methlbl) + populateTypeParamParents(tw, meth.Type().(*types.Signature).RecvTypeParams(), methlbl) extractObject(tw, meth, methlbl) } @@ -1512,7 +1525,12 @@ func extractType(tw *trap.Writer, tp types.Type) trap.Label { } case *types.TypeParam: kind = dbscheme.TypeParamType.Index() - dbscheme.TypeParamTable.Emit(tw, lbl, tp.Obj().Name(), extractType(tw, tp.Constraint())) + parentlbl, exists := tw.TypeParamParent[tp] + if !exists { + log.Fatalf("Parent of type parameter does not exist: %s", tp.String()) + } + constraintLabel := extractType(tw, tp.Constraint()) + dbscheme.TypeParamTable.Emit(tw, lbl, tp.Obj().Name(), constraintLabel, parentlbl, tp.Index()) case *types.Union: kind = dbscheme.TypeSetLiteral.Index() for i := 0; i < tp.Len(); i++ { @@ -1651,7 +1669,8 @@ func getTypeLabel(tw *trap.Writer, tp types.Type) (trap.Label, bool) { } lbl = tw.Labeler.GlobalID(fmt.Sprintf("{%s};namedtype", entitylbl)) case *types.TypeParam: - lbl = tw.Labeler.GlobalID(fmt.Sprintf("{%s},{%s};typeparamtype", tp.Obj().Name(), extractType(tw, tp.Constraint()))) + parentlbl := tw.TypeParamParent[tp] + lbl = tw.Labeler.GlobalID(fmt.Sprintf("{%v},%s;typeparamtype", parentlbl, tp.Obj().Name())) case *types.Union: var b strings.Builder for i := 0; i < tp.Len(); i++ { @@ -1828,3 +1847,12 @@ func extractTypeParamDecls(tw *trap.Writer, fields *ast.FieldList, parent trap.L idx += 1 } } + +// populateTypeParamParents sets `parentlbl` as the parent of the elements of `typeparams` +func populateTypeParamParents(tw *trap.Writer, typeparams *types.TypeParamList, parentlbl trap.Label) { + if typeparams != nil { + for idx := 0; idx < typeparams.Len(); idx++ { + tw.TypeParamParent[typeparams.At(idx)] = parentlbl + } + } +} diff --git a/extractor/trap/trapwriter.go b/extractor/trap/trapwriter.go index 299147e8a53..7647086b4dd 100644 --- a/extractor/trap/trapwriter.go +++ b/extractor/trap/trapwriter.go @@ -18,14 +18,15 @@ import ( // A Writer provides methods for writing data to a TRAP file type Writer struct { - zip *gzip.Writer - w *bufio.Writer - file *os.File - Labeler *Labeler - path string - trapFilePath string - Package *packages.Package - TypesOverride map[ast.Expr]types.Type + zip *gzip.Writer + w *bufio.Writer + file *os.File + Labeler *Labeler + path string + trapFilePath string + Package *packages.Package + TypesOverride map[ast.Expr]types.Type + TypeParamParent map[*types.TypeParam]Label } func FileFor(path string) (string, error) { @@ -64,6 +65,7 @@ func NewWriter(path string, pkg *packages.Package) (*Writer, error) { trapFilePath, pkg, make(map[ast.Expr]types.Type), + make(map[*types.TypeParam]Label), } tw.Labeler = newLabeler(tw) return tw, nil diff --git a/ql/lib/go.dbscheme b/ql/lib/go.dbscheme index 3110ca88d98..90fa7836e0a 100644 --- a/ql/lib/go.dbscheme +++ b/ql/lib/go.dbscheme @@ -227,7 +227,9 @@ has_ellipsis(int id: @callorconversionexpr ref); variadic(int id: @signaturetype ref); -typeparam(unique int tp: @typeparamtype ref, string name: string ref, int bound: @compositetype 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); @container = @file | @folder; @@ -248,7 +250,7 @@ typeparam(unique int tp: @typeparamtype ref, string name: string ref, int bound: @declparent = @file | @declstmt; -@typeparamdeclparent = @functypeexpr | @typespec; +@typeparamdeclparent = @funcdecl | @typespec; @funcdef = @funclit | @funcdecl; @@ -439,6 +441,8 @@ case @object.kind of | 7 = @builtinfunctionobject | 8 = @labelobject; +@typeparamparentobject = @decltypeobject | @declfunctionobject; + @declobject = @decltypeobject | @declconstobject | @declvarobject | @declfunctionobject; @builtinobject = @builtintypeobject | @builtinconstobject | @builtinfunctionobject; diff --git a/ql/lib/semmle/go/Types.qll b/ql/lib/semmle/go/Types.qll index 21ad88e83d7..ce1a5e8fa86 100644 --- a/ql/lib/semmle/go/Types.qll +++ b/ql/lib/semmle/go/Types.qll @@ -367,10 +367,10 @@ 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() } From 63d1663eb2be02e4eff5fc271401f21a621cd8e5 Mon Sep 17 00:00:00 2001 From: Owen Mansel-Chan Date: Thu, 31 Mar 2022 16:55:47 +0100 Subject: [PATCH 13/49] bug fix: label pointer-typed receivers correctly We were trying to convert the object's type to a named type to iterate through its methods, forgetting that it could also be a pointer to a named type. This bug was exposed because we no longer extract an object's type before extracting it (unless it is a receiver), and when we extracted a named type we extract its methods and when extracting a method we extract its receiver and we always give it the correct label in that situation. --- extractor/trap/labels.go | 31 ++++++++++++++++++++----------- 1 file changed, 20 insertions(+), 11 deletions(-) diff --git a/extractor/trap/labels.go b/extractor/trap/labels.go index b42c8e54721..54420adb34d 100644 --- a/extractor/trap/labels.go +++ b/extractor/trap/labels.go @@ -154,20 +154,17 @@ func (l *Labeler) ScopedObjectID(object types.Object, getTypeLabel func() Label) } else { // associate method receiver objects to special keys, because those can be // referenced from other files via their method - isRecv := false - if namedType, ok := object.Type().(*types.Named); ok { - for i := 0; i < namedType.NumMethods(); i++ { - meth := namedType.Method(i) - if object == meth.Type().(*types.Signature).Recv() { - isRecv = true - methlbl, _ := l.MethodID(meth, getTypeLabel()) - label, _ = l.ReceiverObjectID(object, methlbl) - break - } + meth := findMethodWithGivenReceiver(object.Type(), object) + if meth == nil { + if pointerType, ok := object.Type().(*types.Pointer); ok { + meth = findMethodWithGivenReceiver(pointerType.Elem(), object) } } - if !isRecv { + if meth != nil { + methlbl, _ := l.MethodID(meth, getTypeLabel()) + label, _ = l.ReceiverObjectID(object, methlbl) + } else { scopeLbl := l.ScopeID(scope, object.Pkg()) label = l.GlobalID(fmt.Sprintf("{%v},%s;object", scopeLbl, object.Name())) } @@ -177,6 +174,18 @@ func (l *Labeler) ScopedObjectID(object types.Object, getTypeLabel func() Label) return label, exists } +func findMethodWithGivenReceiver(tp types.Type, object types.Object) *types.Func { + if namedType, ok := tp.(*types.Named); ok { + for i := 0; i < namedType.NumMethods(); i++ { + meth := namedType.Method(i) + if object == meth.Type().(*types.Signature).Recv() { + return meth + } + } + } + return nil +} + // ReceiverObjectID associates a label with the given object and returns it, together with a flag indicating whether // the object already had a label associated with it; the object must be the receiver of `methlbl`, since that label // is used to construct the label of the object From 4d9937d1c66c6ab92d9d76cf60e3c8ac76880627 Mon Sep 17 00:00:00 2001 From: Owen Mansel-Chan Date: Tue, 15 Feb 2022 14:04:08 +0000 Subject: [PATCH 14/49] Add tests --- .../GenericFunctionInstantiationExpr.expected | 6 + .../GenericFunctionInstantiationExpr.ql | 4 + .../semmle/go/Function/TypeParamType.expected | 18 +++ .../semmle/go/Function/TypeParamType.ql | 4 + .../semmle/go/Function/genericFunctions.go | 149 ++++++++++++++++++ .../semmle/go/Function/getParameter.expected | 12 ++ .../go/Function/getTypeParameter.expected | 17 ++ .../semmle/go/Function/getTypeParameter.ql | 6 + .../semmle/go/Types/Field_getPackage.expected | 11 ++ .../go/Types/Field_hasQualifiedName2.expected | 11 ++ .../go/Types/Field_hasQualifiedName3.expected | 11 ++ .../GenericTypeInstantiationExpr.expected | 16 ++ .../go/Types/GenericTypeInstantiationExpr.ql | 4 + .../go/Types/ImplementsComparable.expected | 0 .../semmle/go/Types/ImplementsComparable.ql | 21 +++ .../semmle/go/Types/MethodCount.expected | 12 ++ .../semmle/go/Types/MethodTypes.expected | 32 ++++ .../Types/Method_hasQualifiedName2.expected | 32 ++++ .../Types/Method_hasQualifiedName3.expected | 32 ++++ .../semmle/go/Types/Methods.expected | 32 ++++ .../semmle/go/Types/QualifiedNames.expected | 66 ++++++++ .../semmle/go/Types/StructFields.expected | 11 ++ .../semmle/go/Types/Types.expected | 66 ++++++++ .../library-tests/semmle/go/Types/generic.go | 68 ++++++++ .../semmle/go/Types/interface.go | 134 ++++++++++++++++ 25 files changed, 775 insertions(+) create mode 100644 ql/test/library-tests/semmle/go/Function/GenericFunctionInstantiationExpr.expected create mode 100644 ql/test/library-tests/semmle/go/Function/GenericFunctionInstantiationExpr.ql create mode 100644 ql/test/library-tests/semmle/go/Function/TypeParamType.expected create mode 100644 ql/test/library-tests/semmle/go/Function/TypeParamType.ql create mode 100644 ql/test/library-tests/semmle/go/Function/genericFunctions.go create mode 100644 ql/test/library-tests/semmle/go/Function/getTypeParameter.expected create mode 100644 ql/test/library-tests/semmle/go/Function/getTypeParameter.ql create mode 100644 ql/test/library-tests/semmle/go/Types/GenericTypeInstantiationExpr.expected create mode 100644 ql/test/library-tests/semmle/go/Types/GenericTypeInstantiationExpr.ql create mode 100644 ql/test/library-tests/semmle/go/Types/ImplementsComparable.expected create mode 100644 ql/test/library-tests/semmle/go/Types/ImplementsComparable.ql create mode 100644 ql/test/library-tests/semmle/go/Types/generic.go create mode 100644 ql/test/library-tests/semmle/go/Types/interface.go diff --git a/ql/test/library-tests/semmle/go/Function/GenericFunctionInstantiationExpr.expected b/ql/test/library-tests/semmle/go/Function/GenericFunctionInstantiationExpr.expected new file mode 100644 index 00000000000..8ee6c980c10 --- /dev/null +++ b/ql/test/library-tests/semmle/go/Function/GenericFunctionInstantiationExpr.expected @@ -0,0 +1,6 @@ +| genericFunctions.go:23:2:23:33 | generic function instantiation expression | genericFunctions.go:23:2:23:28 | GenericFunctionOneTypeParam | 0 | genericFunctions.go:23:30:23:32 | int | +| genericFunctions.go:24:2:24:36 | generic function instantiation expression | genericFunctions.go:24:2:24:28 | GenericFunctionOneTypeParam | 0 | genericFunctions.go:24:30:24:35 | string | +| genericFunctions.go:42:6:42:48 | generic function instantiation expression | genericFunctions.go:42:6:42:33 | GenericFunctionTwoTypeParams | 0 | genericFunctions.go:42:35:42:40 | string | +| genericFunctions.go:42:6:42:48 | generic function instantiation expression | genericFunctions.go:42:6:42:33 | GenericFunctionTwoTypeParams | 1 | genericFunctions.go:42:43:42:47 | int64 | +| genericFunctions.go:43:6:43:50 | generic function instantiation expression | genericFunctions.go:43:6:43:33 | GenericFunctionTwoTypeParams | 0 | genericFunctions.go:43:35:43:40 | string | +| genericFunctions.go:43:6:43:50 | generic function instantiation expression | genericFunctions.go:43:6:43:33 | GenericFunctionTwoTypeParams | 1 | genericFunctions.go:43:43:43:49 | float64 | diff --git a/ql/test/library-tests/semmle/go/Function/GenericFunctionInstantiationExpr.ql b/ql/test/library-tests/semmle/go/Function/GenericFunctionInstantiationExpr.ql new file mode 100644 index 00000000000..db3da8f82bc --- /dev/null +++ b/ql/test/library-tests/semmle/go/Function/GenericFunctionInstantiationExpr.ql @@ -0,0 +1,4 @@ +import go + +from GenericFunctionInstantiationExpr e, int i +select e, e.getBase(), i, e.getTypeArgument(i) diff --git a/ql/test/library-tests/semmle/go/Function/TypeParamType.expected b/ql/test/library-tests/semmle/go/Function/TypeParamType.expected new file mode 100644 index 00000000000..6c1437fd468 --- /dev/null +++ b/ql/test/library-tests/semmle/go/Function/TypeParamType.expected @@ -0,0 +1,18 @@ +| A | comparable | +| B1 | interface { } | +| B2 | interface { } | +| Edge | EdgeConstraint | +| Edge | interface { } | +| K | comparable | +| Node | NodeConstraint | +| Node | interface { } | +| S | interface { } | +| SF2 | interface { } | +| SG2 | interface { } | +| T | interface { } | +| TF1 | interface { } | +| TF2 | interface { } | +| TG1 | interface { } | +| TG2 | interface { } | +| U | interface { } | +| V | interface { int64 \| float64 } | diff --git a/ql/test/library-tests/semmle/go/Function/TypeParamType.ql b/ql/test/library-tests/semmle/go/Function/TypeParamType.ql new file mode 100644 index 00000000000..a4167f8d702 --- /dev/null +++ b/ql/test/library-tests/semmle/go/Function/TypeParamType.ql @@ -0,0 +1,4 @@ +import go + +from TypeParamType tpt +select tpt.getParamName(), tpt.getConstraint().pp() diff --git a/ql/test/library-tests/semmle/go/Function/genericFunctions.go b/ql/test/library-tests/semmle/go/Function/genericFunctions.go new file mode 100644 index 00000000000..f43fa1d02d9 --- /dev/null +++ b/ql/test/library-tests/semmle/go/Function/genericFunctions.go @@ -0,0 +1,149 @@ +package main + +type T1 map[string][]string +type T2 T1 + +// A generic function with one type parameter +func GenericFunctionOneTypeParam[T any](t T) T { + var r T + return r +} + +// A generic function with two type parameter +func GenericFunctionTwoTypeParams[K comparable, V int64 | float64](m map[K]V) V { + var s V + for _, v := range m { + s += v + } + return s +} + +func generic_functions() { + // Call the generic function with one type parameter, specifying the type + GenericFunctionOneTypeParam[int](1) + GenericFunctionOneTypeParam[string]("hello") + + // Call the generic function with one type parameter, without specifying the type + GenericFunctionOneTypeParam(1) + GenericFunctionOneTypeParam("hello") + + // Initialize a map for the integer values + ints := map[string]int64{ + "first": 34, + "second": 12, + } + + // Initialize a map for the float values + floats := map[string]float64{ + "first": 35.98, + "second": 26.99, + } + + _ = GenericFunctionTwoTypeParams[string, int64](ints) + _ = GenericFunctionTwoTypeParams[string, float64](floats) + + _ = GenericFunctionTwoTypeParams(ints) + _ = GenericFunctionTwoTypeParams(floats) + + // Map read + _ = floats["first"] + // Map write + floats["first"] = -1.0 + + arrayVar := [5]int{1, 2, 3, 4, 5} + // Array read + _ = arrayVar[0] + // Array write + arrayVar[0] = -1 + + arrayPtr := &arrayVar + // Array read through pointer + _ = arrayPtr[0] + // Array write through pointer + arrayPtr[0] = -1 + + sliceVar := make([]int, 0, 5) // $ isVariadic + // Slice read + _ = sliceVar[0] + // Slice write + sliceVar[0] = -1 + + // Access a map through two type aliases + aliasedMap := T2{"key": []string{"value"}} + // Map read + _ = aliasedMap["key"] + // Map write + aliasedMap["key"][0] = "new value" +} + +type GenericStruct1[T any] struct { +} + +type GenericStruct2[S, T any] struct { +} + +func (x GenericStruct1[TF1]) f1() TF1 { + var r TF1 + return r +} + +func (x *GenericStruct1[TG1]) g1() { +} + +func (x GenericStruct2[SF2, TF2]) f2() { +} + +func (x *GenericStruct2[SG2, TG2]) g2() { +} + +func call_methods_with_generic_receiver() { + x1 := GenericStruct1[int]{} + x1.f1() + x1.g1() + + x2 := GenericStruct2[string, int]{} + x2.f2() + x2.g2() +} + +type Element[S any] struct { + list *List[S] +} + +type List[T any] struct { + root Element[T] +} + +// Len is the number of elements in the list. +func (l *List[U]) MyLen() int { + return 0 +} + +func outerFunction[A comparable](x1 A) { + type innerStruct[B1 any] struct { + x1 A + x2 B1 + } + type innerMap[B2 any] map[A]B2 + + var x innerStruct[string] + _ = x + var y innerMap[bool] + _ = y +} + +type NodeConstraint[Edge any] interface { + Edges() []Edge +} + +type EdgeConstraint[Node any] interface { + Nodes() (from, to Node) +} + +type Graph[Node NodeConstraint[Edge], Edge EdgeConstraint[Node]] struct{} + +func New[Node NodeConstraint[Edge], Edge EdgeConstraint[Node]](nodes []Node) *Graph[Node, Edge] { + return nil +} + +func (g *Graph[Node, Edge]) ShortestPath(from, to Node) []Edge { return []Edge{} } diff --git a/ql/test/library-tests/semmle/go/Function/getParameter.expected b/ql/test/library-tests/semmle/go/Function/getParameter.expected index d3a16330977..7b7ab23e09d 100644 --- a/ql/test/library-tests/semmle/go/Function/getParameter.expected +++ b/ql/test/library-tests/semmle/go/Function/getParameter.expected @@ -1,3 +1,15 @@ +| genericFunctions.go:7:6:7:32 | GenericFunctionOneTypeParam | 0 | genericFunctions.go:7:41:7:41 | t | +| genericFunctions.go:13:6:13:33 | GenericFunctionTwoTypeParams | 0 | genericFunctions.go:13:68:13:68 | m | +| genericFunctions.go:85:30:85:31 | f1 | -1 | genericFunctions.go:85:7:85:7 | x | +| genericFunctions.go:90:31:90:32 | g1 | -1 | genericFunctions.go:90:7:90:7 | x | +| genericFunctions.go:93:35:93:36 | f2 | -1 | genericFunctions.go:93:7:93:7 | x | +| genericFunctions.go:96:36:96:37 | g2 | -1 | genericFunctions.go:96:7:96:7 | x | +| genericFunctions.go:118:19:118:23 | MyLen | -1 | genericFunctions.go:118:7:118:7 | l | +| genericFunctions.go:122:6:122:18 | outerFunction | 0 | genericFunctions.go:122:34:122:35 | x1 | +| genericFunctions.go:145:6:145:8 | New | 0 | genericFunctions.go:145:64:145:68 | nodes | +| genericFunctions.go:149:29:149:40 | ShortestPath | 0 | genericFunctions.go:149:42:149:45 | from | +| genericFunctions.go:149:29:149:40 | ShortestPath | 1 | genericFunctions.go:149:48:149:49 | to | +| genericFunctions.go:149:29:149:40 | ShortestPath | -1 | genericFunctions.go:149:7:149:7 | g | | 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 | diff --git a/ql/test/library-tests/semmle/go/Function/getTypeParameter.expected b/ql/test/library-tests/semmle/go/Function/getTypeParameter.expected new file mode 100644 index 00000000000..383938aaff5 --- /dev/null +++ b/ql/test/library-tests/semmle/go/Function/getTypeParameter.expected @@ -0,0 +1,17 @@ +| genericFunctions.go:7:1:10:1 | function declaration | FuncDecl | 0 | genericFunctions.go:7:34:7:38 | type parameter declaration | 0 | genericFunctions.go:7:34:7:34 | T | genericFunctions.go:7:36:7:38 | any | interface { } | +| genericFunctions.go:13:1:19:1 | function declaration | FuncDecl | 0 | genericFunctions.go:13:35:13:46 | type parameter declaration | 0 | genericFunctions.go:13:35:13:35 | K | genericFunctions.go:13:37:13:46 | comparable | comparable | +| genericFunctions.go:13:1:19:1 | function declaration | FuncDecl | 1 | genericFunctions.go:13:49:13:65 | type parameter declaration | 0 | genericFunctions.go:13:49:13:49 | V | genericFunctions.go:13:51:13:65 | type set literal | interface { int64 \| float64 } | +| genericFunctions.go:79:6:80:1 | type declaration specifier | TypeSpec | 0 | genericFunctions.go:79:21:79:25 | type parameter declaration | 0 | genericFunctions.go:79:21:79:21 | T | genericFunctions.go:79:23:79:25 | any | interface { } | +| genericFunctions.go:82:6:83:1 | type declaration specifier | TypeSpec | 0 | genericFunctions.go:82:21:82:28 | type parameter declaration | 0 | genericFunctions.go:82:21:82:21 | S | genericFunctions.go:82:26:82:28 | any | interface { } | +| genericFunctions.go:82:6:83:1 | type declaration specifier | TypeSpec | 0 | genericFunctions.go:82:21:82:28 | type parameter declaration | 1 | genericFunctions.go:82:24:82:24 | T | genericFunctions.go:82:26:82:28 | any | interface { } | +| genericFunctions.go:109:6:111:1 | type declaration specifier | TypeSpec | 0 | genericFunctions.go:109:14:109:18 | type parameter declaration | 0 | genericFunctions.go:109:14:109:14 | S | genericFunctions.go:109:16:109:18 | any | interface { } | +| genericFunctions.go:113:6:115:1 | type declaration specifier | TypeSpec | 0 | genericFunctions.go:113:11:113:15 | type parameter declaration | 0 | genericFunctions.go:113:11:113:11 | T | genericFunctions.go:113:13:113:15 | any | interface { } | +| genericFunctions.go:122:1:133:1 | function declaration | FuncDecl | 0 | genericFunctions.go:122:20:122:31 | type parameter declaration | 0 | genericFunctions.go:122:20:122:20 | A | genericFunctions.go:122:22:122:31 | comparable | comparable | +| genericFunctions.go:123:7:126:2 | type declaration specifier | TypeSpec | 0 | genericFunctions.go:123:19:123:24 | type parameter declaration | 0 | genericFunctions.go:123:19:123:20 | B1 | genericFunctions.go:123:22:123:24 | any | interface { } | +| genericFunctions.go:127:7:127:31 | type declaration specifier | TypeSpec | 0 | genericFunctions.go:127:16:127:21 | type parameter declaration | 0 | genericFunctions.go:127:16:127:17 | B2 | genericFunctions.go:127:19:127:21 | any | interface { } | +| genericFunctions.go:135:6:137:1 | type declaration specifier | TypeSpec | 0 | genericFunctions.go:135:21:135:28 | type parameter declaration | 0 | genericFunctions.go:135:21:135:24 | Edge | genericFunctions.go:135:26:135:28 | any | interface { } | +| genericFunctions.go:139:6:141:1 | type declaration specifier | TypeSpec | 0 | genericFunctions.go:139:21:139:28 | type parameter declaration | 0 | genericFunctions.go:139:21:139:24 | Node | genericFunctions.go:139:26:139:28 | any | interface { } | +| genericFunctions.go:143:6:143:73 | type declaration specifier | TypeSpec | 0 | genericFunctions.go:143:12:143:36 | type parameter declaration | 0 | genericFunctions.go:143:12:143:15 | Node | genericFunctions.go:143:17:143:36 | generic type instantiation expression | NodeConstraint | +| genericFunctions.go:143:6:143:73 | type declaration specifier | TypeSpec | 1 | genericFunctions.go:143:39:143:63 | type parameter declaration | 0 | genericFunctions.go:143:39:143:42 | Edge | genericFunctions.go:143:44:143:63 | generic type instantiation expression | EdgeConstraint | +| genericFunctions.go:145:1:147:1 | function declaration | FuncDecl | 0 | genericFunctions.go:145:10:145:34 | type parameter declaration | 0 | genericFunctions.go:145:10:145:13 | Node | genericFunctions.go:145:15:145:34 | generic type instantiation expression | NodeConstraint | +| genericFunctions.go:145:1:147:1 | function declaration | FuncDecl | 1 | genericFunctions.go:145:37:145:61 | type parameter declaration | 0 | genericFunctions.go:145:37:145:40 | Edge | genericFunctions.go:145:42:145:61 | generic type instantiation expression | EdgeConstraint | diff --git a/ql/test/library-tests/semmle/go/Function/getTypeParameter.ql b/ql/test/library-tests/semmle/go/Function/getTypeParameter.ql new file mode 100644 index 00000000000..d22b6f9885f --- /dev/null +++ b/ql/test/library-tests/semmle/go/Function/getTypeParameter.ql @@ -0,0 +1,6 @@ +import go + +from TypeParamDeclParent x, int i, TypeParamDecl tpd, int j +where tpd = x.getTypeParameterDecl(i) +select x, x.getPrimaryQlClasses(), i, tpd, j, tpd.getNameExpr(j), tpd.getTypeConstraintExpr(), + tpd.getTypeConstraint().pp() diff --git a/ql/test/library-tests/semmle/go/Types/Field_getPackage.expected b/ql/test/library-tests/semmle/go/Types/Field_getPackage.expected index 18a29cc61a6..e635a046b23 100644 --- a/ql/test/library-tests/semmle/go/Types/Field_getPackage.expected +++ b/ql/test/library-tests/semmle/go/Types/Field_getPackage.expected @@ -12,6 +12,17 @@ | embedded.go:8:3:8:5 | Baz | package github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types | | embedded.go:13:2:13:4 | Qux | package github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types | | embedded.go:14:2:14:4 | Baz | package github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types | +| generic.go:4:2:4:11 | valueField | package github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types | +| generic.go:5:2:5:13 | pointerField | package github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types | +| generic.go:6:2:6:11 | arrayField | package github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types | +| generic.go:7:2:7:11 | sliceField | package github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types | +| generic.go:8:2:8:9 | mapField | package github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types | +| generic.go:12:2:12:13 | pointerField | package github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types | +| generic.go:16:2:16:5 | root | package github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types | +| generic.go:20:2:20:12 | structField | package github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types | +| generic.go:21:2:21:9 | mapField | package github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types | +| generic.go:25:2:25:12 | structField | package github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types | +| generic.go:29:2:29:13 | pointerField | package github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types | | pkg1/embedding.go:19:23:19:26 | base | package github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types/pkg1 | | pkg1/embedding.go:22:27:22:30 | base | package github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types/pkg1 | | pkg1/embedding.go:25:24:25:31 | embedder | package github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types/pkg1 | diff --git a/ql/test/library-tests/semmle/go/Types/Field_hasQualifiedName2.expected b/ql/test/library-tests/semmle/go/Types/Field_hasQualifiedName2.expected index 6baf1c54d70..2ac146f266a 100644 --- a/ql/test/library-tests/semmle/go/Types/Field_hasQualifiedName2.expected +++ b/ql/test/library-tests/semmle/go/Types/Field_hasQualifiedName2.expected @@ -19,6 +19,17 @@ | embedded.go:8:3:8:5 | Baz | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types.Qux | Baz | | embedded.go:13:2:13:4 | Qux | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types.EmbedsBaz | Qux | | embedded.go:14:2:14:4 | Baz | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types.EmbedsBaz | Baz | +| generic.go:4:2:4:11 | valueField | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types.GenericStruct1 | valueField | +| generic.go:5:2:5:13 | pointerField | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types.GenericStruct1 | pointerField | +| generic.go:6:2:6:11 | arrayField | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types.GenericStruct1 | arrayField | +| generic.go:7:2:7:11 | sliceField | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types.GenericStruct1 | sliceField | +| generic.go:8:2:8:9 | mapField | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types.GenericStruct1 | mapField | +| generic.go:12:2:12:13 | pointerField | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types.CircularGenericStruct1 | pointerField | +| generic.go:16:2:16:5 | root | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types.UsesCircularGenericStruct1 | root | +| generic.go:20:2:20:12 | structField | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types.GenericStruct2 | structField | +| generic.go:21:2:21:9 | mapField | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types.GenericStruct2 | mapField | +| generic.go:25:2:25:12 | structField | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types.GenericStruct2b | structField | +| generic.go:29:2:29:13 | pointerField | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types.CircularGenericStruct2 | pointerField | | pkg1/embedding.go:19:23:19:26 | base | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types/pkg1.embedder | base | | pkg1/embedding.go:19:23:19:26 | base | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types/pkg1.embedder2 | base | | pkg1/embedding.go:19:23:19:26 | base | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types/pkg1.embedder3 | base | diff --git a/ql/test/library-tests/semmle/go/Types/Field_hasQualifiedName3.expected b/ql/test/library-tests/semmle/go/Types/Field_hasQualifiedName3.expected index 97524ed3f0f..acdddfe867e 100644 --- a/ql/test/library-tests/semmle/go/Types/Field_hasQualifiedName3.expected +++ b/ql/test/library-tests/semmle/go/Types/Field_hasQualifiedName3.expected @@ -19,6 +19,17 @@ | embedded.go:8:3:8:5 | Baz | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types | Qux | Baz | | embedded.go:13:2:13:4 | Qux | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types | EmbedsBaz | Qux | | embedded.go:14:2:14:4 | Baz | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types | EmbedsBaz | Baz | +| generic.go:4:2:4:11 | valueField | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types | GenericStruct1 | valueField | +| generic.go:5:2:5:13 | pointerField | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types | GenericStruct1 | pointerField | +| generic.go:6:2:6:11 | arrayField | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types | GenericStruct1 | arrayField | +| generic.go:7:2:7:11 | sliceField | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types | GenericStruct1 | sliceField | +| generic.go:8:2:8:9 | mapField | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types | GenericStruct1 | mapField | +| generic.go:12:2:12:13 | pointerField | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types | CircularGenericStruct1 | pointerField | +| generic.go:16:2:16:5 | root | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types | UsesCircularGenericStruct1 | root | +| generic.go:20:2:20:12 | structField | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types | GenericStruct2 | structField | +| generic.go:21:2:21:9 | mapField | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types | GenericStruct2 | mapField | +| generic.go:25:2:25:12 | structField | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types | GenericStruct2b | structField | +| generic.go:29:2:29:13 | pointerField | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types | CircularGenericStruct2 | pointerField | | pkg1/embedding.go:19:23:19:26 | base | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types/pkg1 | embedder | base | | pkg1/embedding.go:19:23:19:26 | base | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types/pkg1 | embedder2 | base | | pkg1/embedding.go:19:23:19:26 | base | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types/pkg1 | embedder3 | base | diff --git a/ql/test/library-tests/semmle/go/Types/GenericTypeInstantiationExpr.expected b/ql/test/library-tests/semmle/go/Types/GenericTypeInstantiationExpr.expected new file mode 100644 index 00000000000..b73d611fac2 --- /dev/null +++ b/ql/test/library-tests/semmle/go/Types/GenericTypeInstantiationExpr.expected @@ -0,0 +1,16 @@ +| generic.go:12:16:12:40 | generic type instantiation expression | generic.go:12:16:12:37 | CircularGenericStruct1 | 0 | generic.go:12:39:12:39 | T | +| generic.go:16:7:16:31 | generic type instantiation expression | generic.go:16:7:16:28 | CircularGenericStruct1 | 0 | generic.go:16:30:16:30 | T | +| generic.go:20:14:20:30 | generic type instantiation expression | generic.go:20:14:20:27 | GenericStruct1 | 0 | generic.go:20:29:20:29 | S | +| generic.go:25:14:25:33 | generic type instantiation expression | generic.go:25:14:25:27 | GenericStruct2 | 0 | generic.go:25:29:25:29 | S | +| generic.go:25:14:25:33 | generic type instantiation expression | generic.go:25:14:25:27 | GenericStruct2 | 1 | generic.go:25:32:25:32 | T | +| generic.go:29:16:29:43 | generic type instantiation expression | generic.go:29:16:29:37 | CircularGenericStruct2 | 0 | generic.go:29:39:29:39 | S | +| generic.go:29:16:29:43 | generic type instantiation expression | generic.go:29:16:29:37 | CircularGenericStruct2 | 1 | generic.go:29:42:29:42 | T | +| generic.go:48:10:48:23 | generic type instantiation expression | generic.go:48:10:48:20 | MyInterface | 0 | generic.go:48:22:48:22 | U | +| generic.go:56:12:56:26 | generic type instantiation expression | generic.go:56:12:56:23 | GenericArray | 0 | generic.go:56:25:56:25 | U | +| generic.go:57:12:57:28 | generic type instantiation expression | generic.go:57:12:57:25 | GenericPointer | 0 | generic.go:57:27:57:27 | U | +| generic.go:58:12:58:26 | generic type instantiation expression | generic.go:58:12:58:23 | GenericSlice | 0 | generic.go:58:25:58:25 | U | +| generic.go:59:12:59:25 | generic type instantiation expression | generic.go:59:12:59:22 | GenericMap1 | 0 | generic.go:59:24:59:24 | U | +| generic.go:60:12:60:28 | generic type instantiation expression | generic.go:60:12:60:22 | GenericMap2 | 0 | generic.go:60:24:60:24 | U | +| generic.go:60:12:60:28 | generic type instantiation expression | generic.go:60:12:60:22 | GenericMap2 | 1 | generic.go:60:27:60:27 | U | +| generic.go:61:12:61:28 | generic type instantiation expression | generic.go:61:12:61:25 | GenericChannel | 0 | generic.go:61:27:61:27 | U | +| generic.go:62:12:62:26 | generic type instantiation expression | generic.go:62:12:62:23 | GenericNamed | 0 | generic.go:62:25:62:25 | U | diff --git a/ql/test/library-tests/semmle/go/Types/GenericTypeInstantiationExpr.ql b/ql/test/library-tests/semmle/go/Types/GenericTypeInstantiationExpr.ql new file mode 100644 index 00000000000..fd7ddf99993 --- /dev/null +++ b/ql/test/library-tests/semmle/go/Types/GenericTypeInstantiationExpr.ql @@ -0,0 +1,4 @@ +import go + +from GenericTypeInstantiationExpr e, int i +select e, e.getBase(), i, e.getTypeArgument(i) diff --git a/ql/test/library-tests/semmle/go/Types/ImplementsComparable.expected b/ql/test/library-tests/semmle/go/Types/ImplementsComparable.expected new file mode 100644 index 00000000000..e69de29bb2d diff --git a/ql/test/library-tests/semmle/go/Types/ImplementsComparable.ql b/ql/test/library-tests/semmle/go/Types/ImplementsComparable.ql new file mode 100644 index 00000000000..b73b711169a --- /dev/null +++ b/ql/test/library-tests/semmle/go/Types/ImplementsComparable.ql @@ -0,0 +1,21 @@ +import go +import TestUtilities.InlineExpectationsTest + +class ImplementsComparableTest extends InlineExpectationsTest { + ImplementsComparableTest() { this = "ImplementsComparableTest" } + + override string getARelevantTag() { result = "implementsComparable" } + + override predicate hasActualResult(string file, int line, string element, string tag, string value) { + // file = "interface.go" and + tag = "implementsComparable" and + exists(TypeSpec ts | + ts.getName().matches("testComparable%") and + ts.getATypeParameterDecl().getTypeConstraint().implementsComparable() + | + ts.hasLocationInfo(file, line, _, _, _) and + element = ts.getName() and + value = "" + ) + } +} diff --git a/ql/test/library-tests/semmle/go/Types/MethodCount.expected b/ql/test/library-tests/semmle/go/Types/MethodCount.expected index aa600fe82dc..2b6dc32cada 100644 --- a/ql/test/library-tests/semmle/go/Types/MethodCount.expected +++ b/ql/test/library-tests/semmle/go/Types/MethodCount.expected @@ -13,6 +13,8 @@ | B | 2 | | C | 2 | | Foo | 1 | +| GenericInterface | 1 | +| MyInterface | 17 | | T | 1 | | T3 | 1 | | T4 | 1 | @@ -20,4 +22,14 @@ | embedder | 1 | | embedder2 | 1 | | embedder3 | 1 | +| i6 | 1 | +| i7 | 1 | +| i8 | 2 | +| i9 | 2 | +| i14 | 2 | +| i15 | 2 | +| i17 | 1 | +| i18 | 1 | +| i19 | 1 | +| i20 | 1 | | ptrembedder | 2 | diff --git a/ql/test/library-tests/semmle/go/Types/MethodTypes.expected b/ql/test/library-tests/semmle/go/Types/MethodTypes.expected index a30d9f893c8..2364eb291f8 100644 --- a/ql/test/library-tests/semmle/go/Types/MethodTypes.expected +++ b/ql/test/library-tests/semmle/go/Types/MethodTypes.expected @@ -10,6 +10,24 @@ | B | n | func() | | C | n | func() | | C | o | func() | +| GenericInterface | GetT | func() T | +| MyInterface | clone | func() MyInterface | +| MyInterface | dummy1 | func() [10]U | +| MyInterface | dummy2 | func() * U | +| MyInterface | dummy3 | func() []U | +| MyInterface | dummy4 | func() [U]U | +| MyInterface | dummy5 | func() chan<- U | +| MyInterface | dummy6 | func() MyMapType | +| MyInterface | dummy7 | func() MyFuncType2 | +| MyInterface | dummy11 | func() GenericArray | +| MyInterface | dummy12 | func() GenericPointer | +| MyInterface | dummy13 | func() GenericSlice | +| MyInterface | dummy14 | func() GenericMap1 | +| MyInterface | dummy15 | func() GenericMap2 | +| MyInterface | dummy17 | func() GenericChannel | +| MyInterface | dummy18 | func() GenericNamed | +| MyInterface | dummy19 | func() MyFuncType1 | +| MyInterface | dummy20 | func() MyFuncType2 | | T | half | func() Foo | | T3 | half | func() Foo | | T4 | half | func() Foo | @@ -17,5 +35,19 @@ | embedder2 | f | func() int | | embedder3 | f | func() int | | embedder4 | f | func() int | +| i6 | String | func() string | +| i7 | String | func() string | +| i8 | String | func() string | +| i8 | StringA | func() string | +| i9 | String | func() string | +| i9 | StringB | func() string | +| i14 | String | func() string | +| i14 | StringA | func() string | +| i15 | String | func() string | +| i15 | StringB | func() string | +| i17 | StringA | func() string | +| i18 | StringA | func() string | +| i19 | StringB | func() string | +| i20 | StringB | func() string | | ptrembedder | f | func() int | | ptrembedder | g | func() int | diff --git a/ql/test/library-tests/semmle/go/Types/Method_hasQualifiedName2.expected b/ql/test/library-tests/semmle/go/Types/Method_hasQualifiedName2.expected index 415f31ef447..24eac7cebc1 100644 --- a/ql/test/library-tests/semmle/go/Types/Method_hasQualifiedName2.expected +++ b/ql/test/library-tests/semmle/go/Types/Method_hasQualifiedName2.expected @@ -1,3 +1,35 @@ +| generic.go:33:2:33:5 | GetT | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types | GenericInterface | GetT | +| generic.go:48:2:48:6 | clone | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types | MyInterface | clone | +| generic.go:49:2:49:7 | dummy1 | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types | MyInterface | dummy1 | +| generic.go:50:2:50:7 | dummy2 | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types | MyInterface | dummy2 | +| generic.go:51:2:51:7 | dummy3 | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types | MyInterface | dummy3 | +| generic.go:52:2:52:7 | dummy4 | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types | MyInterface | dummy4 | +| generic.go:53:2:53:7 | dummy5 | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types | MyInterface | dummy5 | +| generic.go:54:2:54:7 | dummy6 | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types | MyInterface | dummy6 | +| generic.go:55:2:55:7 | dummy7 | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types | MyInterface | dummy7 | +| generic.go:56:2:56:8 | dummy11 | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types | MyInterface | dummy11 | +| generic.go:57:2:57:8 | dummy12 | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types | MyInterface | dummy12 | +| generic.go:58:2:58:8 | dummy13 | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types | MyInterface | dummy13 | +| generic.go:59:2:59:8 | dummy14 | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types | MyInterface | dummy14 | +| generic.go:60:2:60:8 | dummy15 | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types | MyInterface | dummy15 | +| generic.go:61:2:61:8 | dummy17 | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types | MyInterface | dummy17 | +| generic.go:62:2:62:8 | dummy18 | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types | MyInterface | dummy18 | +| generic.go:63:2:63:8 | dummy19 | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types | MyInterface | dummy19 | +| generic.go:64:2:64:8 | dummy20 | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types | MyInterface | dummy20 | +| interface.go:30:2:30:7 | String | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types | i6 | String | +| interface.go:30:2:30:7 | String | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types | i8 | String | +| interface.go:30:2:30:7 | String | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types | i9 | String | +| interface.go:30:2:30:7 | String | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types | i14 | String | +| interface.go:30:2:30:7 | String | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types | i15 | String | +| interface.go:37:2:37:7 | String | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types | i7 | String | +| interface.go:43:2:43:8 | StringA | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types | i8 | StringA | +| interface.go:43:2:43:8 | StringA | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types | i14 | StringA | +| interface.go:49:2:49:8 | StringB | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types | i9 | StringB | +| interface.go:49:2:49:8 | StringB | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types | i15 | StringB | +| interface.go:92:2:92:8 | StringA | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types | i17 | StringA | +| interface.go:97:2:97:8 | StringA | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types | i18 | StringA | +| interface.go:102:2:102:8 | StringB | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types | i19 | StringB | +| interface.go:107:2:107:8 | StringB | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types | i20 | StringB | | pkg1/embedding.go:10:13:10:13 | f | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types/pkg1 | base | f | | pkg1/embedding.go:10:13:10:13 | f | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types/pkg1 | embedder | f | | pkg1/embedding.go:10:13:10:13 | f | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types/pkg1 | embedder2 | f | diff --git a/ql/test/library-tests/semmle/go/Types/Method_hasQualifiedName3.expected b/ql/test/library-tests/semmle/go/Types/Method_hasQualifiedName3.expected index 415f31ef447..24eac7cebc1 100644 --- a/ql/test/library-tests/semmle/go/Types/Method_hasQualifiedName3.expected +++ b/ql/test/library-tests/semmle/go/Types/Method_hasQualifiedName3.expected @@ -1,3 +1,35 @@ +| generic.go:33:2:33:5 | GetT | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types | GenericInterface | GetT | +| generic.go:48:2:48:6 | clone | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types | MyInterface | clone | +| generic.go:49:2:49:7 | dummy1 | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types | MyInterface | dummy1 | +| generic.go:50:2:50:7 | dummy2 | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types | MyInterface | dummy2 | +| generic.go:51:2:51:7 | dummy3 | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types | MyInterface | dummy3 | +| generic.go:52:2:52:7 | dummy4 | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types | MyInterface | dummy4 | +| generic.go:53:2:53:7 | dummy5 | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types | MyInterface | dummy5 | +| generic.go:54:2:54:7 | dummy6 | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types | MyInterface | dummy6 | +| generic.go:55:2:55:7 | dummy7 | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types | MyInterface | dummy7 | +| generic.go:56:2:56:8 | dummy11 | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types | MyInterface | dummy11 | +| generic.go:57:2:57:8 | dummy12 | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types | MyInterface | dummy12 | +| generic.go:58:2:58:8 | dummy13 | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types | MyInterface | dummy13 | +| generic.go:59:2:59:8 | dummy14 | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types | MyInterface | dummy14 | +| generic.go:60:2:60:8 | dummy15 | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types | MyInterface | dummy15 | +| generic.go:61:2:61:8 | dummy17 | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types | MyInterface | dummy17 | +| generic.go:62:2:62:8 | dummy18 | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types | MyInterface | dummy18 | +| generic.go:63:2:63:8 | dummy19 | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types | MyInterface | dummy19 | +| generic.go:64:2:64:8 | dummy20 | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types | MyInterface | dummy20 | +| interface.go:30:2:30:7 | String | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types | i6 | String | +| interface.go:30:2:30:7 | String | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types | i8 | String | +| interface.go:30:2:30:7 | String | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types | i9 | String | +| interface.go:30:2:30:7 | String | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types | i14 | String | +| interface.go:30:2:30:7 | String | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types | i15 | String | +| interface.go:37:2:37:7 | String | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types | i7 | String | +| interface.go:43:2:43:8 | StringA | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types | i8 | StringA | +| interface.go:43:2:43:8 | StringA | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types | i14 | StringA | +| interface.go:49:2:49:8 | StringB | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types | i9 | StringB | +| interface.go:49:2:49:8 | StringB | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types | i15 | StringB | +| interface.go:92:2:92:8 | StringA | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types | i17 | StringA | +| interface.go:97:2:97:8 | StringA | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types | i18 | StringA | +| interface.go:102:2:102:8 | StringB | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types | i19 | StringB | +| interface.go:107:2:107:8 | StringB | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types | i20 | StringB | | pkg1/embedding.go:10:13:10:13 | f | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types/pkg1 | base | f | | pkg1/embedding.go:10:13:10:13 | f | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types/pkg1 | embedder | f | | pkg1/embedding.go:10:13:10:13 | f | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types/pkg1 | embedder2 | f | diff --git a/ql/test/library-tests/semmle/go/Types/Methods.expected b/ql/test/library-tests/semmle/go/Types/Methods.expected index b051d1b66a6..8b2406ec858 100644 --- a/ql/test/library-tests/semmle/go/Types/Methods.expected +++ b/ql/test/library-tests/semmle/go/Types/Methods.expected @@ -23,6 +23,24 @@ | C | n | pkg1/interfaces.go:13:2:13:2 | n | | C | o | pkg1/interfaces.go:14:2:14:2 | o | | Foo | half | pkg1/tst.go:33:16:33:19 | half | +| GenericInterface | GetT | generic.go:33:2:33:5 | GetT | +| MyInterface | clone | generic.go:48:2:48:6 | clone | +| MyInterface | dummy1 | generic.go:49:2:49:7 | dummy1 | +| MyInterface | dummy2 | generic.go:50:2:50:7 | dummy2 | +| MyInterface | dummy3 | generic.go:51:2:51:7 | dummy3 | +| MyInterface | dummy4 | generic.go:52:2:52:7 | dummy4 | +| MyInterface | dummy5 | generic.go:53:2:53:7 | dummy5 | +| MyInterface | dummy6 | generic.go:54:2:54:7 | dummy6 | +| MyInterface | dummy7 | generic.go:55:2:55:7 | dummy7 | +| MyInterface | dummy11 | generic.go:56:2:56:8 | dummy11 | +| MyInterface | dummy12 | generic.go:57:2:57:8 | dummy12 | +| MyInterface | dummy13 | generic.go:58:2:58:8 | dummy13 | +| MyInterface | dummy14 | generic.go:59:2:59:8 | dummy14 | +| MyInterface | dummy15 | generic.go:60:2:60:8 | dummy15 | +| MyInterface | dummy17 | generic.go:61:2:61:8 | dummy17 | +| MyInterface | dummy18 | generic.go:62:2:62:8 | dummy18 | +| MyInterface | dummy19 | generic.go:63:2:63:8 | dummy19 | +| MyInterface | dummy20 | generic.go:64:2:64:8 | dummy20 | | T | half | pkg1/tst.go:33:16:33:19 | half | | T3 | half | pkg1/tst.go:33:16:33:19 | half | | T4 | half | pkg1/tst.go:33:16:33:19 | half | @@ -30,5 +48,19 @@ | embedder | f | pkg1/embedding.go:10:13:10:13 | f | | embedder2 | f | pkg1/embedding.go:10:13:10:13 | f | | embedder3 | f | pkg1/embedding.go:30:18:30:18 | f | +| i6 | String | interface.go:30:2:30:7 | String | +| i7 | String | interface.go:37:2:37:7 | String | +| i8 | String | interface.go:30:2:30:7 | String | +| i8 | StringA | interface.go:43:2:43:8 | StringA | +| i9 | String | interface.go:30:2:30:7 | String | +| i9 | StringB | interface.go:49:2:49:8 | StringB | +| i14 | String | interface.go:30:2:30:7 | String | +| i14 | StringA | interface.go:43:2:43:8 | StringA | +| i15 | String | interface.go:30:2:30:7 | String | +| i15 | StringB | interface.go:49:2:49:8 | StringB | +| i17 | StringA | interface.go:92:2:92:8 | StringA | +| i18 | StringA | interface.go:97:2:97:8 | StringA | +| i19 | StringB | interface.go:102:2:102:8 | StringB | +| i20 | StringB | interface.go:107:2:107:8 | StringB | | ptrembedder | f | pkg1/embedding.go:10:13:10:13 | f | | ptrembedder | g | pkg1/embedding.go:14:14:14:14 | g | diff --git a/ql/test/library-tests/semmle/go/Types/QualifiedNames.expected b/ql/test/library-tests/semmle/go/Types/QualifiedNames.expected index 3eda347c40f..d5ea0214f9f 100644 --- a/ql/test/library-tests/semmle/go/Types/QualifiedNames.expected +++ b/ql/test/library-tests/semmle/go/Types/QualifiedNames.expected @@ -7,15 +7,35 @@ | Bar | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types/pkg1.Bar | | Baz | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types.Baz | | C | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types/pkg1.C | +| CircularGenericStruct1 | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types.CircularGenericStruct1 | +| CircularGenericStruct2 | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types.CircularGenericStruct2 | | EmbedsBaz | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types.EmbedsBaz | | Foo | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types/pkg1.Foo | | G | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types/pkg2.G | +| GenericArray | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types.GenericArray | +| GenericChannel | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types.GenericChannel | +| GenericInterface | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types.GenericInterface | +| GenericMap1 | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types.GenericMap1 | +| GenericMap2 | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types.GenericMap2 | +| GenericNamed | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types.GenericNamed | +| GenericPointer | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types.GenericPointer | +| GenericSlice | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types.GenericSlice | +| GenericStruct1 | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types.GenericStruct1 | +| GenericStruct2 | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types.GenericStruct2 | +| GenericStruct2b | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types.GenericStruct2b | +| HasBlankTypeParam | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types.HasBlankTypeParam | +| HasBlankTypeParams | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types.HasBlankTypeParams | +| MyFuncType1 | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types.MyFuncType1 | +| MyFuncType2 | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types.MyFuncType2 | +| MyInterface | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types.MyInterface | +| MyMapType | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types.MyMapType | | Qux | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types.Qux | | T | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types/pkg1.T | | T | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types/pkg2.T | | T2 | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types/pkg1.T2 | | T3 | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types/pkg1.T3 | | T4 | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types/pkg1.T4 | +| UsesCircularGenericStruct1 | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types.UsesCircularGenericStruct1 | | a | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types.a | | b | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types.b | | base | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types/pkg1.base | @@ -25,8 +45,54 @@ | embedder2 | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types/pkg1.embedder2 | | embedder3 | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types/pkg1.embedder3 | | embedder4 | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types/pkg1.embedder4 | +| i0 | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types.i0 | +| i1 | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types.i1 | +| i2 | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types.i2 | +| i3 | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types.i3 | +| i4 | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types.i4 | +| i5 | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types.i5 | +| i6 | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types.i6 | +| i7 | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types.i7 | +| i8 | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types.i8 | +| i9 | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types.i9 | +| i10 | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types.i10 | +| i11 | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types.i11 | +| i12 | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types.i12 | +| i13 | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types.i13 | +| i14 | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types.i14 | +| i15 | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types.i15 | +| i16 | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types.i16 | +| i17 | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types.i17 | +| i18 | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types.i18 | +| i19 | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types.i19 | +| i20 | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types.i20 | | ptrembedder | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types/pkg1.ptrembedder | | s | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types.s | | t | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types.t | +| testComparable | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types.testComparable | +| testComparable0 | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types.testComparable0 | +| testComparable1 | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types.testComparable1 | +| testComparable2 | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types.testComparable2 | +| testComparable3 | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types.testComparable3 | +| testComparable4 | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types.testComparable4 | +| testComparable5 | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types.testComparable5 | +| testComparable6 | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types.testComparable6 | +| testComparable7 | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types.testComparable7 | +| testComparable8 | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types.testComparable8 | +| testComparable9 | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types.testComparable9 | +| testComparable10 | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types.testComparable10 | +| testComparable11 | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types.testComparable11 | +| testComparable12 | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types.testComparable12 | +| testComparable13 | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types.testComparable13 | +| testComparable14 | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types.testComparable14 | +| testComparable15 | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types.testComparable15 | +| testComparable16 | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types.testComparable16 | +| testComparable17 | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types.testComparable17 | +| testComparable18 | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types.testComparable18 | +| testComparable19 | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types.testComparable19 | +| testComparable20 | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types.testComparable20 | +| testComparable21 | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types.testComparable21 | +| testComparable22 | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types.testComparable22 | +| testComparable23 | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types.testComparable23 | | u | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types.u | | v | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types.v | diff --git a/ql/test/library-tests/semmle/go/Types/StructFields.expected b/ql/test/library-tests/semmle/go/Types/StructFields.expected index 9bf97529652..5ae7f7e73b7 100644 --- a/ql/test/library-tests/semmle/go/Types/StructFields.expected +++ b/ql/test/library-tests/semmle/go/Types/StructFields.expected @@ -1,11 +1,21 @@ | Bar | pkg1/tst.go:29:10:31:1 | struct type | flag | bool | | Baz | embedded.go:3:10:5:1 | struct type | A | string | +| CircularGenericStruct1 | generic.go:11:38:13:1 | struct type | pointerField | * CircularGenericStruct1 | +| CircularGenericStruct2 | generic.go:28:39:30:1 | struct type | pointerField | * CircularGenericStruct2 | | EmbedsBaz | embedded.go:12:16:15:1 | struct type | Baz | string | | EmbedsBaz | embedded.go:12:16:15:1 | struct type | Qux | Qux | | Foo | pkg1/tst.go:24:10:27:1 | struct type | flag | bool | | Foo | pkg1/tst.go:24:10:27:1 | struct type | val | int | | G | pkg2/tst.go:3:8:5:1 | struct type | g | int | | G | pkg2/tst.go:7:8:9:1 | struct type | g | int | +| GenericStruct1 | generic.go:3:28:9:1 | struct type | arrayField | [10]T | +| GenericStruct1 | generic.go:3:28:9:1 | struct type | mapField | [string]T | +| GenericStruct1 | generic.go:3:28:9:1 | struct type | pointerField | * T | +| GenericStruct1 | generic.go:3:28:9:1 | struct type | sliceField | []T | +| GenericStruct1 | generic.go:3:28:9:1 | struct type | valueField | T | +| GenericStruct2 | generic.go:19:42:22:1 | struct type | mapField | [S]T | +| GenericStruct2 | generic.go:19:42:22:1 | struct type | structField | GenericStruct1 | +| GenericStruct2b | generic.go:24:39:26:1 | struct type | structField | GenericStruct2 | | Qux | embedded.go:7:10:9:1 | struct type | A | string | | Qux | embedded.go:7:10:9:1 | struct type | Baz | * Baz | | T | pkg1/tst.go:3:8:7:1 | struct type | Bar | Bar | @@ -24,6 +34,7 @@ | T4 | pkg1/tst.go:19:9:22:1 | struct type | Foo | * Foo | | T4 | pkg1/tst.go:19:9:22:1 | struct type | flag | bool | | T4 | pkg1/tst.go:19:9:22:1 | struct type | val | int | +| UsesCircularGenericStruct1 | generic.go:15:42:17:1 | struct type | root | CircularGenericStruct1 | | a | depth.go:5:8:8:1 | struct type | b | b | | a | depth.go:5:8:8:1 | struct type | c | c | | a | depth.go:5:8:8:1 | struct type | d | d | diff --git a/ql/test/library-tests/semmle/go/Types/Types.expected b/ql/test/library-tests/semmle/go/Types/Types.expected index 60f9edd55d3..1eed45e95e0 100644 --- a/ql/test/library-tests/semmle/go/Types/Types.expected +++ b/ql/test/library-tests/semmle/go/Types/Types.expected @@ -7,15 +7,35 @@ | Bar | Bar | | Baz | Baz | | C | C | +| CircularGenericStruct1 | CircularGenericStruct1 | +| CircularGenericStruct2 | CircularGenericStruct2 | | EmbedsBaz | EmbedsBaz | | Foo | Foo | | G | G | +| GenericArray | GenericArray | +| GenericChannel | GenericChannel | +| GenericInterface | GenericInterface | +| GenericMap1 | GenericMap1 | +| GenericMap2 | GenericMap2 | +| GenericNamed | GenericNamed | +| GenericPointer | GenericPointer | +| GenericSlice | GenericSlice | +| GenericStruct1 | GenericStruct1 | +| GenericStruct2 | GenericStruct2 | +| GenericStruct2b | GenericStruct2b | +| HasBlankTypeParam | HasBlankTypeParam | +| HasBlankTypeParams | HasBlankTypeParams | +| MyFuncType1 | MyFuncType1 | +| MyFuncType2 | MyFuncType2 | +| MyInterface | MyInterface | +| MyMapType | MyMapType | | Qux | Qux | | T | T | | T | T | | T2 | T2 | | T3 | T3 | | T4 | T4 | +| UsesCircularGenericStruct1 | UsesCircularGenericStruct1 | | a | a | | b | b | | base | base | @@ -25,8 +45,54 @@ | embedder2 | embedder2 | | embedder3 | embedder3 | | embedder4 | embedder4 | +| i0 | i0 | +| i1 | i1 | +| i2 | i2 | +| i3 | i3 | +| i4 | i4 | +| i5 | i5 | +| i6 | i6 | +| i7 | i7 | +| i8 | i8 | +| i9 | i9 | +| i10 | i10 | +| i11 | i11 | +| i12 | i12 | +| i13 | i13 | +| i14 | i14 | +| i15 | i15 | +| i16 | i16 | +| i17 | i17 | +| i18 | i18 | +| i19 | i19 | +| i20 | i20 | | ptrembedder | ptrembedder | | s | s | | t | t | +| testComparable | testComparable | +| testComparable0 | testComparable0 | +| testComparable1 | testComparable1 | +| testComparable2 | testComparable2 | +| testComparable3 | testComparable3 | +| testComparable4 | testComparable4 | +| testComparable5 | testComparable5 | +| testComparable6 | testComparable6 | +| testComparable7 | testComparable7 | +| testComparable8 | testComparable8 | +| testComparable9 | testComparable9 | +| testComparable10 | testComparable10 | +| testComparable11 | testComparable11 | +| testComparable12 | testComparable12 | +| testComparable13 | testComparable13 | +| testComparable14 | testComparable14 | +| testComparable15 | testComparable15 | +| testComparable16 | testComparable16 | +| testComparable17 | testComparable17 | +| testComparable18 | testComparable18 | +| testComparable19 | testComparable19 | +| testComparable20 | testComparable20 | +| testComparable21 | testComparable21 | +| testComparable22 | testComparable22 | +| testComparable23 | testComparable23 | | u | u | | v | v | diff --git a/ql/test/library-tests/semmle/go/Types/generic.go b/ql/test/library-tests/semmle/go/Types/generic.go new file mode 100644 index 00000000000..0a58c3d8f3e --- /dev/null +++ b/ql/test/library-tests/semmle/go/Types/generic.go @@ -0,0 +1,68 @@ +package main + +type GenericStruct1[T any] struct { + valueField T + pointerField *T + arrayField [10]T + sliceField []T + mapField map[string]T +} + +type CircularGenericStruct1[T error] struct { + pointerField *CircularGenericStruct1[T] +} + +type UsesCircularGenericStruct1[T error] struct { + root CircularGenericStruct1[T] +} + +type GenericStruct2[S comparable, T int] struct { + structField GenericStruct1[S] + mapField map[S]T +} + +type GenericStruct2b[S string, T int] struct { + structField GenericStruct2[S, T] +} + +type CircularGenericStruct2[S, T any] struct { + pointerField *CircularGenericStruct2[S, T] +} + +type GenericInterface[T ~int32 | ~int64] interface { + GetT() T +} + +type GenericArray[T comparable] [10]T +type GenericPointer[T any] *T +type GenericSlice[T any] []T +type GenericMap1[V any] map[string]V +type GenericMap2[K comparable, V any] map[K]V +type GenericChannel[T comparable] chan<- T +type MyMapType map[string]int +type GenericNamed[T comparable] MyMapType +type MyFuncType1[T any] func(T) +type MyFuncType2[T1 any, T2 any] func(T1) T2 + +type MyInterface[U comparable] interface { + clone() MyInterface[U] + dummy1() [10]U + dummy2() *U + dummy3() []U + dummy4() map[U]U + dummy5() chan<- U + dummy6() MyMapType + dummy7() MyFuncType2[int, bool] + dummy11() GenericArray[U] + dummy12() GenericPointer[U] + dummy13() GenericSlice[U] + dummy14() GenericMap1[U] + dummy15() GenericMap2[U, U] + dummy17() GenericChannel[U] + dummy18() GenericNamed[U] + dummy19() MyFuncType1[U] + dummy20() MyFuncType2[U, U] +} + +type HasBlankTypeParam[_ any] struct{} +type HasBlankTypeParams[_ any, _ comparable, T ~string] struct{} diff --git a/ql/test/library-tests/semmle/go/Types/interface.go b/ql/test/library-tests/semmle/go/Types/interface.go new file mode 100644 index 00000000000..40653aaca71 --- /dev/null +++ b/ql/test/library-tests/semmle/go/Types/interface.go @@ -0,0 +1,134 @@ +package main + +type i0 comparable + +type i1 interface { + int +} + +type i2 interface { + ~string +} + +type i3 interface { + [5]int | ~string +} + +// typeset: int | ~string | float32 +type i4 interface { + i1 | i2 | float32 +} + +// typeset: []byte +type i5 interface { + int | ~[]byte + []byte +} + +type i6 interface { + ~[]int | ~string + String() string +} + +// typeset: ~string +type i7 interface { + i3 + ~string + String() string +} + +// typeset: ~[]int | ~string +type i8 interface { + i6 + StringA() string +} + +// typeset: ~[]int | ~string +type i9 interface { + i6 + StringB() string +} + +type i10 interface { + comparable +} + +// typeset: the empty set +type i11 interface { + [5]byte | string + int +} + +// typeset: []byte | string +type i12 interface { + []byte | string + comparable +} + +// typeset: []byte | string +type i13 interface { + []byte | string + i10 +} + +// typeset: string +type i14 interface { + []byte | string + i8 +} + +// typeset: string +type i15 interface { + []byte | string + i9 +} + +// The empty interface does not implement comparable +type i16 interface { +} + +// A basic interface, so not comparable +type i17 interface { + StringA() string +} + +type i18 interface { + comparable + StringA() string +} + +// A basic interface, so not comparable +type i19 interface { + StringB() string +} + +type i20 interface { + comparable + StringB() string +} + +type testComparable[T comparable] struct{} // $ implementsComparable +type testComparable0[T0 i0] struct{} // $ implementsComparable +type testComparable1[T1 i1] struct{} // $ implementsComparable +type testComparable2[T2 i2] struct{} // $ implementsComparable +type testComparable3[T3 i3] struct{} // $ implementsComparable +type testComparable4[T4 i4] struct{} // $ implementsComparable +type testComparable5[T5 i5] struct{} // does not implement comparable +type testComparable6[T6 i6] struct{} // does not implement comparable +type testComparable7[T7 i7] struct{} // $ implementsComparable +type testComparable8[T8 i8] struct{} // does not implement comparable +type testComparable9[T9 i9] struct{} // does not implement comparable +type testComparable10[T10 i10] struct{} // $ implementsComparable +type testComparable11[T11 i11] struct{} // $ implementsComparable +type testComparable12[T12 i12] struct{} // does not implement comparable +type testComparable13[T13 i13] struct{} // does not implement comparable +type testComparable14[T14 i14] struct{} // $ implementsComparable +type testComparable15[T15 i15] struct{} // $ implementsComparable +type testComparable16[T16 i16] struct{} // does not implement comparable +type testComparable17[T17 i17] struct{} // does not implement comparable +type testComparable18[T18 i18] struct{} // $ implementsComparable +type testComparable19[T19 i19] struct{} // does not implement comparable +type testComparable20[T20 i20] struct{} // $ implementsComparable +type testComparable21[T21 ~[]byte | string] struct{} // does not implement comparable +type testComparable22[T22 any] struct{} // does not implement comparable +type testComparable23[T23 ~[5]byte | string] struct{} // $ implementsComparable From 0194eb98d7bb129d0d2a7da0f4d9000a1f864ec6 Mon Sep 17 00:00:00 2001 From: Owen Mansel-Chan Date: Fri, 1 Apr 2022 13:55:06 +0100 Subject: [PATCH 15/49] Add an upgrade and downgrade script --- .../exprs.ql | 41 ++ .../go.dbscheme | 531 +++++++++++++++++ .../old.dbscheme | 547 ++++++++++++++++++ .../types.ql | 31 + .../upgrade.properties | 6 + downgrades/initial/go.dbscheme | 531 +++++++++++++++++ downgrades/qlpack.yml | 4 + .../exprs.ql | 33 ++ .../go.dbscheme | 547 ++++++++++++++++++ .../old.dbscheme | 531 +++++++++++++++++ .../types.ql | 21 + .../upgrade.properties | 4 + 12 files changed, 2827 insertions(+) create mode 100644 downgrades/90fa7836e0a239f69bbebffcf342e92c240d54bc/exprs.ql create mode 100644 downgrades/90fa7836e0a239f69bbebffcf342e92c240d54bc/go.dbscheme create mode 100644 downgrades/90fa7836e0a239f69bbebffcf342e92c240d54bc/old.dbscheme create mode 100644 downgrades/90fa7836e0a239f69bbebffcf342e92c240d54bc/types.ql create mode 100644 downgrades/90fa7836e0a239f69bbebffcf342e92c240d54bc/upgrade.properties create mode 100644 downgrades/initial/go.dbscheme create mode 100644 downgrades/qlpack.yml create mode 100644 ql/lib/upgrades/8f168c8af3fee9b57bcfce85bb2ab708a5e3c828/exprs.ql create mode 100644 ql/lib/upgrades/8f168c8af3fee9b57bcfce85bb2ab708a5e3c828/go.dbscheme create mode 100644 ql/lib/upgrades/8f168c8af3fee9b57bcfce85bb2ab708a5e3c828/old.dbscheme create mode 100644 ql/lib/upgrades/8f168c8af3fee9b57bcfce85bb2ab708a5e3c828/types.ql create mode 100644 ql/lib/upgrades/8f168c8af3fee9b57bcfce85bb2ab708a5e3c828/upgrade.properties diff --git a/downgrades/90fa7836e0a239f69bbebffcf342e92c240d54bc/exprs.ql b/downgrades/90fa7836e0a239f69bbebffcf342e92c240d54bc/exprs.ql new file mode 100644 index 00000000000..b4b7498e27c --- /dev/null +++ b/downgrades/90fa7836e0a239f69bbebffcf342e92c240d54bc/exprs.ql @@ -0,0 +1,41 @@ +class Expr_ extends @expr { + string toString() { result = "Expr" } +} + +class ExprParent_ extends @exprparent { + string toString() { result = "ExprParent" } +} + +/** + * Two new kinds have been inserted such that `@sliceexpr` which used to have + * index 13 now has index 15. Another new kind has been inserted such that + * `@plusexpr` which used to have index 23 now has index 26. Entries with + * indices lower than 13 are unchanged. + */ +bindingset[new_index] +int old_index(int new_index) { + if new_index < 13 + then result = new_index + else + if new_index = [13, 14] + then result = 0 // badexpr + else + if new_index < 23 + then result + (15 - 13) = new_index + else + if new_index = 23 + then result = 0 // badexpr + else result + (26 - 23) = new_index +} + +// The schema for exprs is: +// +// exprs(unique int id: @expr, +// int kind: int ref, +// int parent: @exprparent ref, +// int idx: int ref); +from Expr_ expr, int new_kind, ExprParent_ parent, int idx, int old_kind +where + exprs(expr, new_kind, parent, idx) and + old_kind = old_index(new_kind) +select expr, old_kind, parent, idx diff --git a/downgrades/90fa7836e0a239f69bbebffcf342e92c240d54bc/go.dbscheme b/downgrades/90fa7836e0a239f69bbebffcf342e92c240d54bc/go.dbscheme new file mode 100644 index 00000000000..8f168c8af3f --- /dev/null +++ b/downgrades/90fa7836e0a239f69bbebffcf342e92c240d54bc/go.dbscheme @@ -0,0 +1,531 @@ +/** Auto-generated dbscheme; do not edit. */ + + +/** Duplicate code **/ + +duplicateCode( + unique int id : @duplication, + varchar(900) relativePath : string ref, + int equivClass : int ref); + +similarCode( + unique int id : @similarity, + varchar(900) relativePath : string ref, + int equivClass : int ref); + +@duplication_or_similarity = @duplication | @similarity; + +tokens( + int id : @duplication_or_similarity ref, + int offset : int ref, + int beginLine : int ref, + int beginColumn : int ref, + int endLine : int ref, + int endColumn : int ref); + +/** External data **/ + +externalData( + int id : @externalDataElement, + varchar(900) path : string ref, + int column: int ref, + varchar(900) value : string ref +); + +snapshotDate(unique date snapshotDate : date ref); + +sourceLocationPrefix(varchar(900) prefix : string ref); + + +/* + * XML Files + */ + +xmlEncoding( + unique int id: @file ref, + string encoding: string ref +); + +xmlDTDs( + unique int id: @xmldtd, + string root: string ref, + string publicId: string ref, + string systemId: string ref, + int fileid: @file ref +); + +xmlElements( + unique int id: @xmlelement, + string name: string ref, + int parentid: @xmlparent ref, + int idx: int ref, + int fileid: @file ref +); + +xmlAttrs( + unique int id: @xmlattribute, + int elementid: @xmlelement ref, + string name: string ref, + string value: string ref, + int idx: int ref, + int fileid: @file ref +); + +xmlNs( + int id: @xmlnamespace, + string prefixName: string ref, + string URI: string ref, + int fileid: @file ref +); + +xmlHasNs( + int elementId: @xmlnamespaceable ref, + int nsId: @xmlnamespace ref, + int fileid: @file ref +); + +xmlComments( + unique int id: @xmlcomment, + string text: string ref, + int parentid: @xmlparent ref, + int fileid: @file ref +); + +xmlChars( + unique int id: @xmlcharacters, + string text: string ref, + int parentid: @xmlparent ref, + int idx: int ref, + int isCDATA: int ref, + int fileid: @file ref +); + +@xmlparent = @file | @xmlelement; +@xmlnamespaceable = @xmlelement | @xmlattribute; + +xmllocations( + int xmlElement: @xmllocatable ref, + int location: @location_default ref +); + +@xmllocatable = @xmlcharacters | @xmlelement | @xmlcomment | @xmlattribute | @xmldtd | @file | @xmlnamespace; + +compilations(unique int id: @compilation, string cwd: string ref); + +#keyset[id, num] +compilation_args(int id: @compilation ref, int num: int ref, string arg: string ref); + +#keyset[id, num, kind] +compilation_time(int id: @compilation ref, int num: int ref, int kind: int ref, float secs: float ref); + +diagnostic_for(unique int diagnostic: @diagnostic ref, int compilation: @compilation ref, int file_number: int ref, int file_number_diagnostic_number: int ref); + +compilation_finished(unique int id: @compilation ref, float cpu_seconds: float ref, float elapsed_seconds: float ref); + +#keyset[id, num] +compilation_compiling_files(int id: @compilation ref, int num: int ref, int file: @file ref); + +diagnostics(unique int id: @diagnostic, int severity: int ref, string error_tag: string ref, string error_message: string ref, + string full_error_message: string ref, int location: @location ref); + +locations_default(unique int id: @location_default, int file: @file ref, int beginLine: int ref, int beginColumn: int ref, + int endLine: int ref, int endColumn: int ref); + +numlines(int element_id: @sourceline ref, int num_lines: int ref, int num_code: int ref, int num_comment: int ref); + +files(unique int id: @file, string name: string ref); + +folders(unique int id: @folder, string name: string ref); + +containerparent(int parent: @container ref, unique int child: @container ref); + +has_location(unique int locatable: @locatable ref, int location: @location ref); + +#keyset[parent, idx] +comment_groups(unique int id: @comment_group, int parent: @file ref, int idx: int ref); + +comments(unique int id: @comment, int kind: int ref, int parent: @comment_group ref, int idx: int ref, string text: string ref); + +doc_comments(unique int node: @documentable ref, int comment: @comment_group ref); + +#keyset[parent, idx] +exprs(unique int id: @expr, int kind: int ref, int parent: @exprparent ref, int idx: int ref); + +literals(unique int expr: @expr ref, string value: string ref, string raw: string ref); + +constvalues(unique int expr: @expr ref, string value: string ref, string exact: string ref); + +fields(unique int id: @field, int parent: @fieldparent ref, int idx: int ref); + +#keyset[parent, idx] +stmts(unique int id: @stmt, int kind: int ref, int parent: @stmtparent ref, int idx: int ref); + +#keyset[parent, idx] +decls(unique int id: @decl, int kind: int ref, int parent: @declparent ref, int idx: int ref); + +#keyset[parent, idx] +specs(unique int id: @spec, int kind: int ref, int parent: @gendecl ref, int idx: int ref); + +scopes(unique int id: @scope, int kind: int ref); + +scopenesting(unique int inner: @scope ref, int outer: @scope ref); + +scopenodes(unique int node: @scopenode ref, int scope: @localscope ref); + +objects(unique int id: @object, int kind: int ref, string name: string ref); + +objectscopes(unique int object: @object ref, int scope: @scope ref); + +objecttypes(unique int object: @object ref, int tp: @type ref); + +methodreceivers(unique int method: @object ref, int receiver: @object ref); + +fieldstructs(unique int field: @object ref, int struct: @structtype ref); + +methodhosts(int method: @object ref, int host: @namedtype ref); + +defs(int ident: @ident ref, int object: @object ref); + +uses(int ident: @ident ref, int object: @object ref); + +types(unique int id: @type, int kind: int ref); + +type_of(unique int expr: @expr ref, int tp: @type ref); + +typename(unique int tp: @type ref, string name: string ref); + +key_type(unique int map: @maptype ref, int tp: @type ref); + +element_type(unique int container: @containertype ref, int tp: @type ref); + +base_type(unique int ptr: @pointertype ref, int tp: @type ref); + +underlying_type(unique int named: @namedtype ref, int tp: @type ref); + +#keyset[parent, index] +component_types(int parent: @compositetype ref, int index: int ref, string name: string ref, int tp: @type ref); + +array_length(unique int tp: @arraytype ref, string len: string ref); + +type_objects(unique int tp: @type ref, int object: @object ref); + +packages(unique int id: @package, string name: string ref, string path: string ref, int scope: @packagescope ref); + +#keyset[parent, idx] +modexprs(unique int id: @modexpr, int kind: int ref, int parent: @modexprparent ref, int idx: int ref); + +#keyset[parent, idx] +modtokens(string token: string ref, int parent: @modexpr ref, int idx: int ref); + +#keyset[package, idx] +errors(unique int id: @error, int kind: int ref, string msg: string ref, string rawpos: string ref, + string file: string ref, int line: int ref, int col: int ref, int package: @package ref, int idx: int ref); + +has_ellipsis(int id: @callorconversionexpr ref); + +variadic(int id: @signaturetype ref); + +@container = @file | @folder; + +@locatable = @xmllocatable | @node | @localscope; + +@node = @documentable | @exprparent | @modexprparent | @fieldparent | @stmtparent | @declparent | @scopenode + | @comment_group | @comment; + +@documentable = @file | @field | @spec | @gendecl | @funcdecl | @modexpr; + +@exprparent = @funcdef | @file | @expr | @field | @stmt | @decl | @spec; + +@modexprparent = @file | @modexpr; + +@fieldparent = @decl | @structtypeexpr | @functypeexpr | @interfacetypeexpr; + +@stmtparent = @funcdef | @stmt | @decl; + +@declparent = @file | @declstmt; + +@funcdef = @funclit | @funcdecl; + +@scopenode = @file | @functypeexpr | @blockstmt | @ifstmt | @caseclause | @switchstmt | @commclause | @loopstmt; + +@location = @location_default; + +@sourceline = @locatable; + +case @comment.kind of + 0 = @slashslashcomment +| 1 = @slashstarcomment; + +case @expr.kind of + 0 = @badexpr +| 1 = @ident +| 2 = @ellipsis +| 3 = @intlit +| 4 = @floatlit +| 5 = @imaglit +| 6 = @charlit +| 7 = @stringlit +| 8 = @funclit +| 9 = @compositelit +| 10 = @parenexpr +| 11 = @selectorexpr +| 12 = @indexexpr +| 13 = @sliceexpr +| 14 = @typeassertexpr +| 15 = @callorconversionexpr +| 16 = @starexpr +| 17 = @keyvalueexpr +| 18 = @arraytypeexpr +| 19 = @structtypeexpr +| 20 = @functypeexpr +| 21 = @interfacetypeexpr +| 22 = @maptypeexpr +| 23 = @plusexpr +| 24 = @minusexpr +| 25 = @notexpr +| 26 = @complementexpr +| 27 = @derefexpr +| 28 = @addressexpr +| 29 = @arrowexpr +| 30 = @lorexpr +| 31 = @landexpr +| 32 = @eqlexpr +| 33 = @neqexpr +| 34 = @lssexpr +| 35 = @leqexpr +| 36 = @gtrexpr +| 37 = @geqexpr +| 38 = @addexpr +| 39 = @subexpr +| 40 = @orexpr +| 41 = @xorexpr +| 42 = @mulexpr +| 43 = @quoexpr +| 44 = @remexpr +| 45 = @shlexpr +| 46 = @shrexpr +| 47 = @andexpr +| 48 = @andnotexpr +| 49 = @sendchantypeexpr +| 50 = @recvchantypeexpr +| 51 = @sendrcvchantypeexpr +| 52 = @errorexpr; + +@basiclit = @intlit | @floatlit | @imaglit | @charlit | @stringlit; + +@operatorexpr = @logicalexpr | @arithmeticexpr | @bitwiseexpr | @unaryexpr | @binaryexpr; + +@logicalexpr = @logicalunaryexpr | @logicalbinaryexpr; + +@arithmeticexpr = @arithmeticunaryexpr | @arithmeticbinaryexpr; + +@bitwiseexpr = @bitwiseunaryexpr | @bitwisebinaryexpr; + +@unaryexpr = @logicalunaryexpr | @bitwiseunaryexpr | @arithmeticunaryexpr | @derefexpr | @addressexpr | @arrowexpr; + +@logicalunaryexpr = @notexpr; + +@bitwiseunaryexpr = @complementexpr; + +@arithmeticunaryexpr = @plusexpr | @minusexpr; + +@binaryexpr = @logicalbinaryexpr | @bitwisebinaryexpr | @arithmeticbinaryexpr | @comparison; + +@logicalbinaryexpr = @lorexpr | @landexpr; + +@bitwisebinaryexpr = @shiftexpr | @orexpr | @xorexpr | @andexpr | @andnotexpr; + +@arithmeticbinaryexpr = @addexpr | @subexpr | @mulexpr | @quoexpr | @remexpr; + +@shiftexpr = @shlexpr | @shrexpr; + +@comparison = @equalitytest | @relationalcomparison; + +@equalitytest = @eqlexpr | @neqexpr; + +@relationalcomparison = @lssexpr | @leqexpr | @gtrexpr | @geqexpr; + +@chantypeexpr = @sendchantypeexpr | @recvchantypeexpr | @sendrcvchantypeexpr; + +case @stmt.kind of + 0 = @badstmt +| 1 = @declstmt +| 2 = @emptystmt +| 3 = @labeledstmt +| 4 = @exprstmt +| 5 = @sendstmt +| 6 = @incstmt +| 7 = @decstmt +| 8 = @gostmt +| 9 = @deferstmt +| 10 = @returnstmt +| 11 = @breakstmt +| 12 = @continuestmt +| 13 = @gotostmt +| 14 = @fallthroughstmt +| 15 = @blockstmt +| 16 = @ifstmt +| 17 = @caseclause +| 18 = @exprswitchstmt +| 19 = @typeswitchstmt +| 20 = @commclause +| 21 = @selectstmt +| 22 = @forstmt +| 23 = @rangestmt +| 24 = @assignstmt +| 25 = @definestmt +| 26 = @addassignstmt +| 27 = @subassignstmt +| 28 = @mulassignstmt +| 29 = @quoassignstmt +| 30 = @remassignstmt +| 31 = @andassignstmt +| 32 = @orassignstmt +| 33 = @xorassignstmt +| 34 = @shlassignstmt +| 35 = @shrassignstmt +| 36 = @andnotassignstmt; + +@incdecstmt = @incstmt | @decstmt; + +@assignment = @simpleassignstmt | @compoundassignstmt; + +@simpleassignstmt = @assignstmt | @definestmt; + +@compoundassignstmt = @addassignstmt | @subassignstmt | @mulassignstmt | @quoassignstmt | @remassignstmt + | @andassignstmt | @orassignstmt | @xorassignstmt | @shlassignstmt | @shrassignstmt | @andnotassignstmt; + +@branchstmt = @breakstmt | @continuestmt | @gotostmt | @fallthroughstmt; + +@switchstmt = @exprswitchstmt | @typeswitchstmt; + +@loopstmt = @forstmt | @rangestmt; + +case @decl.kind of + 0 = @baddecl +| 1 = @importdecl +| 2 = @constdecl +| 3 = @typedecl +| 4 = @vardecl +| 5 = @funcdecl; + +@gendecl = @importdecl | @constdecl | @typedecl | @vardecl; + +case @spec.kind of + 0 = @importspec +| 1 = @valuespec +| 2 = @typedefspec +| 3 = @aliasspec; + +@typespec = @typedefspec | @aliasspec; + +case @object.kind of + 0 = @pkgobject +| 1 = @decltypeobject +| 2 = @builtintypeobject +| 3 = @declconstobject +| 4 = @builtinconstobject +| 5 = @declvarobject +| 6 = @declfunctionobject +| 7 = @builtinfunctionobject +| 8 = @labelobject; + +@declobject = @decltypeobject | @declconstobject | @declvarobject | @declfunctionobject; + +@builtinobject = @builtintypeobject | @builtinconstobject | @builtinfunctionobject; + +@typeobject = @decltypeobject | @builtintypeobject; + +@valueobject = @constobject | @varobject | @functionobject; + +@constobject = @declconstobject | @builtinconstobject; + +@varobject = @declvarobject; + +@functionobject = @declfunctionobject | @builtinfunctionobject; + +case @scope.kind of + 0 = @universescope +| 1 = @packagescope +| 2 = @localscope; + +case @type.kind of + 0 = @invalidtype +| 1 = @boolexprtype +| 2 = @inttype +| 3 = @int8type +| 4 = @int16type +| 5 = @int32type +| 6 = @int64type +| 7 = @uinttype +| 8 = @uint8type +| 9 = @uint16type +| 10 = @uint32type +| 11 = @uint64type +| 12 = @uintptrtype +| 13 = @float32type +| 14 = @float64type +| 15 = @complex64type +| 16 = @complex128type +| 17 = @stringexprtype +| 18 = @unsafepointertype +| 19 = @boolliteraltype +| 20 = @intliteraltype +| 21 = @runeliteraltype +| 22 = @floatliteraltype +| 23 = @complexliteraltype +| 24 = @stringliteraltype +| 25 = @nilliteraltype +| 26 = @arraytype +| 27 = @slicetype +| 28 = @structtype +| 29 = @pointertype +| 30 = @interfacetype +| 31 = @tupletype +| 32 = @signaturetype +| 33 = @maptype +| 34 = @sendchantype +| 35 = @recvchantype +| 36 = @sendrcvchantype +| 37 = @namedtype; + +@basictype = @booltype | @numerictype | @stringtype | @literaltype | @invalidtype | @unsafepointertype; + +@booltype = @boolexprtype | @boolliteraltype; + +@numerictype = @integertype | @floattype | @complextype; + +@integertype = @signedintegertype | @unsignedintegertype; + +@signedintegertype = @inttype | @int8type | @int16type | @int32type | @int64type | @intliteraltype | @runeliteraltype; + +@unsignedintegertype = @uinttype | @uint8type | @uint16type | @uint32type | @uint64type | @uintptrtype; + +@floattype = @float32type | @float64type | @floatliteraltype; + +@complextype = @complex64type | @complex128type | @complexliteraltype; + +@stringtype = @stringexprtype | @stringliteraltype; + +@literaltype = @boolliteraltype | @intliteraltype | @runeliteraltype | @floatliteraltype | @complexliteraltype + | @stringliteraltype | @nilliteraltype; + +@compositetype = @containertype | @structtype | @pointertype | @interfacetype | @tupletype | @signaturetype | @namedtype; + +@containertype = @arraytype | @slicetype | @maptype | @chantype; + +@chantype = @sendchantype | @recvchantype | @sendrcvchantype; + +case @modexpr.kind of + 0 = @modcommentblock +| 1 = @modline +| 2 = @modlineblock +| 3 = @modlparen +| 4 = @modrparen; + +case @error.kind of + 0 = @unknownerror +| 1 = @listerror +| 2 = @parseerror +| 3 = @typeerror; + diff --git a/downgrades/90fa7836e0a239f69bbebffcf342e92c240d54bc/old.dbscheme b/downgrades/90fa7836e0a239f69bbebffcf342e92c240d54bc/old.dbscheme new file mode 100644 index 00000000000..90fa7836e0a --- /dev/null +++ b/downgrades/90fa7836e0a239f69bbebffcf342e92c240d54bc/old.dbscheme @@ -0,0 +1,547 @@ +/** Auto-generated dbscheme; do not edit. */ + + +/** Duplicate code **/ + +duplicateCode( + unique int id : @duplication, + varchar(900) relativePath : string ref, + int equivClass : int ref); + +similarCode( + unique int id : @similarity, + varchar(900) relativePath : string ref, + int equivClass : int ref); + +@duplication_or_similarity = @duplication | @similarity; + +tokens( + int id : @duplication_or_similarity ref, + int offset : int ref, + int beginLine : int ref, + int beginColumn : int ref, + int endLine : int ref, + int endColumn : int ref); + +/** External data **/ + +externalData( + int id : @externalDataElement, + varchar(900) path : string ref, + int column: int ref, + varchar(900) value : string ref +); + +snapshotDate(unique date snapshotDate : date ref); + +sourceLocationPrefix(varchar(900) prefix : string ref); + + +/* + * XML Files + */ + +xmlEncoding( + unique int id: @file ref, + string encoding: string ref +); + +xmlDTDs( + unique int id: @xmldtd, + string root: string ref, + string publicId: string ref, + string systemId: string ref, + int fileid: @file ref +); + +xmlElements( + unique int id: @xmlelement, + string name: string ref, + int parentid: @xmlparent ref, + int idx: int ref, + int fileid: @file ref +); + +xmlAttrs( + unique int id: @xmlattribute, + int elementid: @xmlelement ref, + string name: string ref, + string value: string ref, + int idx: int ref, + int fileid: @file ref +); + +xmlNs( + int id: @xmlnamespace, + string prefixName: string ref, + string URI: string ref, + int fileid: @file ref +); + +xmlHasNs( + int elementId: @xmlnamespaceable ref, + int nsId: @xmlnamespace ref, + int fileid: @file ref +); + +xmlComments( + unique int id: @xmlcomment, + string text: string ref, + int parentid: @xmlparent ref, + int fileid: @file ref +); + +xmlChars( + unique int id: @xmlcharacters, + string text: string ref, + int parentid: @xmlparent ref, + int idx: int ref, + int isCDATA: int ref, + int fileid: @file ref +); + +@xmlparent = @file | @xmlelement; +@xmlnamespaceable = @xmlelement | @xmlattribute; + +xmllocations( + int xmlElement: @xmllocatable ref, + int location: @location_default ref +); + +@xmllocatable = @xmlcharacters | @xmlelement | @xmlcomment | @xmlattribute | @xmldtd | @file | @xmlnamespace; + +compilations(unique int id: @compilation, string cwd: string ref); + +#keyset[id, num] +compilation_args(int id: @compilation ref, int num: int ref, string arg: string ref); + +#keyset[id, num, kind] +compilation_time(int id: @compilation ref, int num: int ref, int kind: int ref, float secs: float ref); + +diagnostic_for(unique int diagnostic: @diagnostic ref, int compilation: @compilation ref, int file_number: int ref, int file_number_diagnostic_number: int ref); + +compilation_finished(unique int id: @compilation ref, float cpu_seconds: float ref, float elapsed_seconds: float ref); + +#keyset[id, num] +compilation_compiling_files(int id: @compilation ref, int num: int ref, int file: @file ref); + +diagnostics(unique int id: @diagnostic, int severity: int ref, string error_tag: string ref, string error_message: string ref, + string full_error_message: string ref, int location: @location ref); + +locations_default(unique int id: @location_default, int file: @file ref, int beginLine: int ref, int beginColumn: int ref, + int endLine: int ref, int endColumn: int ref); + +numlines(int element_id: @sourceline ref, int num_lines: int ref, int num_code: int ref, int num_comment: int ref); + +files(unique int id: @file, string name: string ref); + +folders(unique int id: @folder, string name: string ref); + +containerparent(int parent: @container ref, unique int child: @container ref); + +has_location(unique int locatable: @locatable ref, int location: @location ref); + +#keyset[parent, idx] +comment_groups(unique int id: @comment_group, int parent: @file ref, int idx: int ref); + +comments(unique int id: @comment, int kind: int ref, int parent: @comment_group ref, int idx: int ref, string text: string ref); + +doc_comments(unique int node: @documentable ref, int comment: @comment_group ref); + +#keyset[parent, idx] +exprs(unique int id: @expr, int kind: int ref, int parent: @exprparent ref, int idx: int ref); + +literals(unique int expr: @expr ref, string value: string ref, string raw: string ref); + +constvalues(unique int expr: @expr ref, string value: string ref, string exact: string ref); + +fields(unique int id: @field, int parent: @fieldparent ref, int idx: int ref); + +typeparamdecls(unique int id: @typeparamdecl, int parent: @typeparamdeclparent ref, int idx: int ref); + +#keyset[parent, idx] +stmts(unique int id: @stmt, int kind: int ref, int parent: @stmtparent ref, int idx: int ref); + +#keyset[parent, idx] +decls(unique int id: @decl, int kind: int ref, int parent: @declparent ref, int idx: int ref); + +#keyset[parent, idx] +specs(unique int id: @spec, int kind: int ref, int parent: @gendecl ref, int idx: int ref); + +scopes(unique int id: @scope, int kind: int ref); + +scopenesting(unique int inner: @scope ref, int outer: @scope ref); + +scopenodes(unique int node: @scopenode ref, int scope: @localscope ref); + +objects(unique int id: @object, int kind: int ref, string name: string ref); + +objectscopes(unique int object: @object ref, int scope: @scope ref); + +objecttypes(unique int object: @object ref, int tp: @type ref); + +methodreceivers(unique int method: @object ref, int receiver: @object ref); + +fieldstructs(unique int field: @object ref, int struct: @structtype ref); + +methodhosts(int method: @object ref, int host: @namedtype ref); + +defs(int ident: @ident ref, int object: @object ref); + +uses(int ident: @ident ref, int object: @object ref); + +types(unique int id: @type, int kind: int ref); + +type_of(unique int expr: @expr ref, int tp: @type ref); + +typename(unique int tp: @type ref, string name: string ref); + +key_type(unique int map: @maptype ref, int tp: @type ref); + +element_type(unique int container: @containertype ref, int tp: @type ref); + +base_type(unique int ptr: @pointertype ref, int tp: @type ref); + +underlying_type(unique int named: @namedtype ref, int tp: @type ref); + +#keyset[parent, index] +component_types(int parent: @compositetype ref, int index: int ref, string name: string ref, int tp: @type ref); + +array_length(unique int tp: @arraytype ref, string len: string ref); + +type_objects(unique int tp: @type ref, int object: @object ref); + +packages(unique int id: @package, string name: string ref, string path: string ref, int scope: @packagescope ref); + +#keyset[parent, idx] +modexprs(unique int id: @modexpr, int kind: int ref, int parent: @modexprparent ref, int idx: int ref); + +#keyset[parent, idx] +modtokens(string token: string ref, int parent: @modexpr ref, int idx: int ref); + +#keyset[package, idx] +errors(unique int id: @error, int kind: int ref, string msg: string ref, string rawpos: string ref, + string file: string ref, int line: int ref, int col: int ref, int package: @package ref, int idx: int ref); + +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); + +@container = @file | @folder; + +@locatable = @xmllocatable | @node | @localscope; + +@node = @documentable | @exprparent | @modexprparent | @fieldparent | @stmtparent | @declparent | @typeparamdeclparent + | @scopenode | @comment_group | @comment; + +@documentable = @file | @field | @typeparamdecl | @spec | @gendecl | @funcdecl | @modexpr; + +@exprparent = @funcdef | @file | @expr | @field | @stmt | @decl | @typeparamdecl | @spec; + +@modexprparent = @file | @modexpr; + +@fieldparent = @decl | @structtypeexpr | @functypeexpr | @interfacetypeexpr; + +@stmtparent = @funcdef | @stmt | @decl; + +@declparent = @file | @declstmt; + +@typeparamdeclparent = @funcdecl | @typespec; + +@funcdef = @funclit | @funcdecl; + +@scopenode = @file | @functypeexpr | @blockstmt | @ifstmt | @caseclause | @switchstmt | @commclause | @loopstmt; + +@location = @location_default; + +@sourceline = @locatable; + +case @comment.kind of + 0 = @slashslashcomment +| 1 = @slashstarcomment; + +case @expr.kind of + 0 = @badexpr +| 1 = @ident +| 2 = @ellipsis +| 3 = @intlit +| 4 = @floatlit +| 5 = @imaglit +| 6 = @charlit +| 7 = @stringlit +| 8 = @funclit +| 9 = @compositelit +| 10 = @parenexpr +| 11 = @selectorexpr +| 12 = @indexexpr +| 13 = @genericfunctioninstantiationexpr +| 14 = @generictypeinstantiationexpr +| 15 = @sliceexpr +| 16 = @typeassertexpr +| 17 = @callorconversionexpr +| 18 = @starexpr +| 19 = @keyvalueexpr +| 20 = @arraytypeexpr +| 21 = @structtypeexpr +| 22 = @functypeexpr +| 23 = @interfacetypeexpr +| 24 = @maptypeexpr +| 25 = @typesetliteralexpr +| 26 = @plusexpr +| 27 = @minusexpr +| 28 = @notexpr +| 29 = @complementexpr +| 30 = @derefexpr +| 31 = @addressexpr +| 32 = @arrowexpr +| 33 = @lorexpr +| 34 = @landexpr +| 35 = @eqlexpr +| 36 = @neqexpr +| 37 = @lssexpr +| 38 = @leqexpr +| 39 = @gtrexpr +| 40 = @geqexpr +| 41 = @addexpr +| 42 = @subexpr +| 43 = @orexpr +| 44 = @xorexpr +| 45 = @mulexpr +| 46 = @quoexpr +| 47 = @remexpr +| 48 = @shlexpr +| 49 = @shrexpr +| 50 = @andexpr +| 51 = @andnotexpr +| 52 = @sendchantypeexpr +| 53 = @recvchantypeexpr +| 54 = @sendrcvchantypeexpr +| 55 = @errorexpr; + +@basiclit = @intlit | @floatlit | @imaglit | @charlit | @stringlit; + +@operatorexpr = @logicalexpr | @arithmeticexpr | @bitwiseexpr | @unaryexpr | @binaryexpr; + +@logicalexpr = @logicalunaryexpr | @logicalbinaryexpr; + +@arithmeticexpr = @arithmeticunaryexpr | @arithmeticbinaryexpr; + +@bitwiseexpr = @bitwiseunaryexpr | @bitwisebinaryexpr; + +@unaryexpr = @logicalunaryexpr | @bitwiseunaryexpr | @arithmeticunaryexpr | @derefexpr | @addressexpr | @arrowexpr; + +@logicalunaryexpr = @notexpr; + +@bitwiseunaryexpr = @complementexpr; + +@arithmeticunaryexpr = @plusexpr | @minusexpr; + +@binaryexpr = @logicalbinaryexpr | @bitwisebinaryexpr | @arithmeticbinaryexpr | @comparison; + +@logicalbinaryexpr = @lorexpr | @landexpr; + +@bitwisebinaryexpr = @shiftexpr | @orexpr | @xorexpr | @andexpr | @andnotexpr; + +@arithmeticbinaryexpr = @addexpr | @subexpr | @mulexpr | @quoexpr | @remexpr; + +@shiftexpr = @shlexpr | @shrexpr; + +@comparison = @equalitytest | @relationalcomparison; + +@equalitytest = @eqlexpr | @neqexpr; + +@relationalcomparison = @lssexpr | @leqexpr | @gtrexpr | @geqexpr; + +@chantypeexpr = @sendchantypeexpr | @recvchantypeexpr | @sendrcvchantypeexpr; + +case @stmt.kind of + 0 = @badstmt +| 1 = @declstmt +| 2 = @emptystmt +| 3 = @labeledstmt +| 4 = @exprstmt +| 5 = @sendstmt +| 6 = @incstmt +| 7 = @decstmt +| 8 = @gostmt +| 9 = @deferstmt +| 10 = @returnstmt +| 11 = @breakstmt +| 12 = @continuestmt +| 13 = @gotostmt +| 14 = @fallthroughstmt +| 15 = @blockstmt +| 16 = @ifstmt +| 17 = @caseclause +| 18 = @exprswitchstmt +| 19 = @typeswitchstmt +| 20 = @commclause +| 21 = @selectstmt +| 22 = @forstmt +| 23 = @rangestmt +| 24 = @assignstmt +| 25 = @definestmt +| 26 = @addassignstmt +| 27 = @subassignstmt +| 28 = @mulassignstmt +| 29 = @quoassignstmt +| 30 = @remassignstmt +| 31 = @andassignstmt +| 32 = @orassignstmt +| 33 = @xorassignstmt +| 34 = @shlassignstmt +| 35 = @shrassignstmt +| 36 = @andnotassignstmt; + +@incdecstmt = @incstmt | @decstmt; + +@assignment = @simpleassignstmt | @compoundassignstmt; + +@simpleassignstmt = @assignstmt | @definestmt; + +@compoundassignstmt = @addassignstmt | @subassignstmt | @mulassignstmt | @quoassignstmt | @remassignstmt + | @andassignstmt | @orassignstmt | @xorassignstmt | @shlassignstmt | @shrassignstmt | @andnotassignstmt; + +@branchstmt = @breakstmt | @continuestmt | @gotostmt | @fallthroughstmt; + +@switchstmt = @exprswitchstmt | @typeswitchstmt; + +@loopstmt = @forstmt | @rangestmt; + +case @decl.kind of + 0 = @baddecl +| 1 = @importdecl +| 2 = @constdecl +| 3 = @typedecl +| 4 = @vardecl +| 5 = @funcdecl; + +@gendecl = @importdecl | @constdecl | @typedecl | @vardecl; + +case @spec.kind of + 0 = @importspec +| 1 = @valuespec +| 2 = @typedefspec +| 3 = @aliasspec; + +@typespec = @typedefspec | @aliasspec; + +case @object.kind of + 0 = @pkgobject +| 1 = @decltypeobject +| 2 = @builtintypeobject +| 3 = @declconstobject +| 4 = @builtinconstobject +| 5 = @declvarobject +| 6 = @declfunctionobject +| 7 = @builtinfunctionobject +| 8 = @labelobject; + +@typeparamparentobject = @decltypeobject | @declfunctionobject; + +@declobject = @decltypeobject | @declconstobject | @declvarobject | @declfunctionobject; + +@builtinobject = @builtintypeobject | @builtinconstobject | @builtinfunctionobject; + +@typeobject = @decltypeobject | @builtintypeobject; + +@valueobject = @constobject | @varobject | @functionobject; + +@constobject = @declconstobject | @builtinconstobject; + +@varobject = @declvarobject; + +@functionobject = @declfunctionobject | @builtinfunctionobject; + +case @scope.kind of + 0 = @universescope +| 1 = @packagescope +| 2 = @localscope; + +case @type.kind of + 0 = @invalidtype +| 1 = @boolexprtype +| 2 = @inttype +| 3 = @int8type +| 4 = @int16type +| 5 = @int32type +| 6 = @int64type +| 7 = @uinttype +| 8 = @uint8type +| 9 = @uint16type +| 10 = @uint32type +| 11 = @uint64type +| 12 = @uintptrtype +| 13 = @float32type +| 14 = @float64type +| 15 = @complex64type +| 16 = @complex128type +| 17 = @stringexprtype +| 18 = @unsafepointertype +| 19 = @boolliteraltype +| 20 = @intliteraltype +| 21 = @runeliteraltype +| 22 = @floatliteraltype +| 23 = @complexliteraltype +| 24 = @stringliteraltype +| 25 = @nilliteraltype +| 26 = @typeparamtype +| 27 = @arraytype +| 28 = @slicetype +| 29 = @structtype +| 30 = @pointertype +| 31 = @interfacetype +| 32 = @tupletype +| 33 = @signaturetype +| 34 = @maptype +| 35 = @sendchantype +| 36 = @recvchantype +| 37 = @sendrcvchantype +| 38 = @namedtype +| 39 = @typesetliteraltype; + +@basictype = @booltype | @numerictype | @stringtype | @literaltype | @invalidtype | @unsafepointertype; + +@booltype = @boolexprtype | @boolliteraltype; + +@numerictype = @integertype | @floattype | @complextype; + +@integertype = @signedintegertype | @unsignedintegertype; + +@signedintegertype = @inttype | @int8type | @int16type | @int32type | @int64type | @intliteraltype | @runeliteraltype; + +@unsignedintegertype = @uinttype | @uint8type | @uint16type | @uint32type | @uint64type | @uintptrtype; + +@floattype = @float32type | @float64type | @floatliteraltype; + +@complextype = @complex64type | @complex128type | @complexliteraltype; + +@stringtype = @stringexprtype | @stringliteraltype; + +@literaltype = @boolliteraltype | @intliteraltype | @runeliteraltype | @floatliteraltype | @complexliteraltype + | @stringliteraltype | @nilliteraltype; + +@compositetype = @typeparamtype | @containertype | @structtype | @pointertype | @interfacetype | @tupletype + | @signaturetype | @namedtype | @typesetliteraltype; + +@containertype = @arraytype | @slicetype | @maptype | @chantype; + +@chantype = @sendchantype | @recvchantype | @sendrcvchantype; + +case @modexpr.kind of + 0 = @modcommentblock +| 1 = @modline +| 2 = @modlineblock +| 3 = @modlparen +| 4 = @modrparen; + +case @error.kind of + 0 = @unknownerror +| 1 = @listerror +| 2 = @parseerror +| 3 = @typeerror; + diff --git a/downgrades/90fa7836e0a239f69bbebffcf342e92c240d54bc/types.ql b/downgrades/90fa7836e0a239f69bbebffcf342e92c240d54bc/types.ql new file mode 100644 index 00000000000..0b02cc68085 --- /dev/null +++ b/downgrades/90fa7836e0a239f69bbebffcf342e92c240d54bc/types.ql @@ -0,0 +1,31 @@ +class Type_ extends @type { + string toString() { result = "Type" } +} + +/** + * A new kind has been inserted such that `@arraytype` which used to have index + * 26 now has index 27. Another new kind has been inserted at 39, which is the + * end of the list. Entries with lower indices are unchanged. + */ +bindingset[new_index] +int old_index(int new_index) { + if new_index < 26 + then result = new_index + else + if new_index = 26 + then result = 0 // invalidtype + else + if new_index < 39 + then result + (27 - 26) = new_index + else result = 0 // invalidtype +} + +// The schema for types is: +// +// types(unique int id: @type, +// int kind: int ref); +from Type_ type, int new_kind, int old_kind +where + types(type, new_kind) and + old_kind = old_index(new_kind) +select type, old_kind diff --git a/downgrades/90fa7836e0a239f69bbebffcf342e92c240d54bc/upgrade.properties b/downgrades/90fa7836e0a239f69bbebffcf342e92c240d54bc/upgrade.properties new file mode 100644 index 00000000000..a233cc281c8 --- /dev/null +++ b/downgrades/90fa7836e0a239f69bbebffcf342e92c240d54bc/upgrade.properties @@ -0,0 +1,6 @@ +description: Add generic instantiation expressions and type parameter types +compatibility: full +exprs.rel: run exprs.qlo +types.rel: run types.qlo +typeparamdecls.rel: delete +typeparam.rel: delete \ No newline at end of file diff --git a/downgrades/initial/go.dbscheme b/downgrades/initial/go.dbscheme new file mode 100644 index 00000000000..8f168c8af3f --- /dev/null +++ b/downgrades/initial/go.dbscheme @@ -0,0 +1,531 @@ +/** Auto-generated dbscheme; do not edit. */ + + +/** Duplicate code **/ + +duplicateCode( + unique int id : @duplication, + varchar(900) relativePath : string ref, + int equivClass : int ref); + +similarCode( + unique int id : @similarity, + varchar(900) relativePath : string ref, + int equivClass : int ref); + +@duplication_or_similarity = @duplication | @similarity; + +tokens( + int id : @duplication_or_similarity ref, + int offset : int ref, + int beginLine : int ref, + int beginColumn : int ref, + int endLine : int ref, + int endColumn : int ref); + +/** External data **/ + +externalData( + int id : @externalDataElement, + varchar(900) path : string ref, + int column: int ref, + varchar(900) value : string ref +); + +snapshotDate(unique date snapshotDate : date ref); + +sourceLocationPrefix(varchar(900) prefix : string ref); + + +/* + * XML Files + */ + +xmlEncoding( + unique int id: @file ref, + string encoding: string ref +); + +xmlDTDs( + unique int id: @xmldtd, + string root: string ref, + string publicId: string ref, + string systemId: string ref, + int fileid: @file ref +); + +xmlElements( + unique int id: @xmlelement, + string name: string ref, + int parentid: @xmlparent ref, + int idx: int ref, + int fileid: @file ref +); + +xmlAttrs( + unique int id: @xmlattribute, + int elementid: @xmlelement ref, + string name: string ref, + string value: string ref, + int idx: int ref, + int fileid: @file ref +); + +xmlNs( + int id: @xmlnamespace, + string prefixName: string ref, + string URI: string ref, + int fileid: @file ref +); + +xmlHasNs( + int elementId: @xmlnamespaceable ref, + int nsId: @xmlnamespace ref, + int fileid: @file ref +); + +xmlComments( + unique int id: @xmlcomment, + string text: string ref, + int parentid: @xmlparent ref, + int fileid: @file ref +); + +xmlChars( + unique int id: @xmlcharacters, + string text: string ref, + int parentid: @xmlparent ref, + int idx: int ref, + int isCDATA: int ref, + int fileid: @file ref +); + +@xmlparent = @file | @xmlelement; +@xmlnamespaceable = @xmlelement | @xmlattribute; + +xmllocations( + int xmlElement: @xmllocatable ref, + int location: @location_default ref +); + +@xmllocatable = @xmlcharacters | @xmlelement | @xmlcomment | @xmlattribute | @xmldtd | @file | @xmlnamespace; + +compilations(unique int id: @compilation, string cwd: string ref); + +#keyset[id, num] +compilation_args(int id: @compilation ref, int num: int ref, string arg: string ref); + +#keyset[id, num, kind] +compilation_time(int id: @compilation ref, int num: int ref, int kind: int ref, float secs: float ref); + +diagnostic_for(unique int diagnostic: @diagnostic ref, int compilation: @compilation ref, int file_number: int ref, int file_number_diagnostic_number: int ref); + +compilation_finished(unique int id: @compilation ref, float cpu_seconds: float ref, float elapsed_seconds: float ref); + +#keyset[id, num] +compilation_compiling_files(int id: @compilation ref, int num: int ref, int file: @file ref); + +diagnostics(unique int id: @diagnostic, int severity: int ref, string error_tag: string ref, string error_message: string ref, + string full_error_message: string ref, int location: @location ref); + +locations_default(unique int id: @location_default, int file: @file ref, int beginLine: int ref, int beginColumn: int ref, + int endLine: int ref, int endColumn: int ref); + +numlines(int element_id: @sourceline ref, int num_lines: int ref, int num_code: int ref, int num_comment: int ref); + +files(unique int id: @file, string name: string ref); + +folders(unique int id: @folder, string name: string ref); + +containerparent(int parent: @container ref, unique int child: @container ref); + +has_location(unique int locatable: @locatable ref, int location: @location ref); + +#keyset[parent, idx] +comment_groups(unique int id: @comment_group, int parent: @file ref, int idx: int ref); + +comments(unique int id: @comment, int kind: int ref, int parent: @comment_group ref, int idx: int ref, string text: string ref); + +doc_comments(unique int node: @documentable ref, int comment: @comment_group ref); + +#keyset[parent, idx] +exprs(unique int id: @expr, int kind: int ref, int parent: @exprparent ref, int idx: int ref); + +literals(unique int expr: @expr ref, string value: string ref, string raw: string ref); + +constvalues(unique int expr: @expr ref, string value: string ref, string exact: string ref); + +fields(unique int id: @field, int parent: @fieldparent ref, int idx: int ref); + +#keyset[parent, idx] +stmts(unique int id: @stmt, int kind: int ref, int parent: @stmtparent ref, int idx: int ref); + +#keyset[parent, idx] +decls(unique int id: @decl, int kind: int ref, int parent: @declparent ref, int idx: int ref); + +#keyset[parent, idx] +specs(unique int id: @spec, int kind: int ref, int parent: @gendecl ref, int idx: int ref); + +scopes(unique int id: @scope, int kind: int ref); + +scopenesting(unique int inner: @scope ref, int outer: @scope ref); + +scopenodes(unique int node: @scopenode ref, int scope: @localscope ref); + +objects(unique int id: @object, int kind: int ref, string name: string ref); + +objectscopes(unique int object: @object ref, int scope: @scope ref); + +objecttypes(unique int object: @object ref, int tp: @type ref); + +methodreceivers(unique int method: @object ref, int receiver: @object ref); + +fieldstructs(unique int field: @object ref, int struct: @structtype ref); + +methodhosts(int method: @object ref, int host: @namedtype ref); + +defs(int ident: @ident ref, int object: @object ref); + +uses(int ident: @ident ref, int object: @object ref); + +types(unique int id: @type, int kind: int ref); + +type_of(unique int expr: @expr ref, int tp: @type ref); + +typename(unique int tp: @type ref, string name: string ref); + +key_type(unique int map: @maptype ref, int tp: @type ref); + +element_type(unique int container: @containertype ref, int tp: @type ref); + +base_type(unique int ptr: @pointertype ref, int tp: @type ref); + +underlying_type(unique int named: @namedtype ref, int tp: @type ref); + +#keyset[parent, index] +component_types(int parent: @compositetype ref, int index: int ref, string name: string ref, int tp: @type ref); + +array_length(unique int tp: @arraytype ref, string len: string ref); + +type_objects(unique int tp: @type ref, int object: @object ref); + +packages(unique int id: @package, string name: string ref, string path: string ref, int scope: @packagescope ref); + +#keyset[parent, idx] +modexprs(unique int id: @modexpr, int kind: int ref, int parent: @modexprparent ref, int idx: int ref); + +#keyset[parent, idx] +modtokens(string token: string ref, int parent: @modexpr ref, int idx: int ref); + +#keyset[package, idx] +errors(unique int id: @error, int kind: int ref, string msg: string ref, string rawpos: string ref, + string file: string ref, int line: int ref, int col: int ref, int package: @package ref, int idx: int ref); + +has_ellipsis(int id: @callorconversionexpr ref); + +variadic(int id: @signaturetype ref); + +@container = @file | @folder; + +@locatable = @xmllocatable | @node | @localscope; + +@node = @documentable | @exprparent | @modexprparent | @fieldparent | @stmtparent | @declparent | @scopenode + | @comment_group | @comment; + +@documentable = @file | @field | @spec | @gendecl | @funcdecl | @modexpr; + +@exprparent = @funcdef | @file | @expr | @field | @stmt | @decl | @spec; + +@modexprparent = @file | @modexpr; + +@fieldparent = @decl | @structtypeexpr | @functypeexpr | @interfacetypeexpr; + +@stmtparent = @funcdef | @stmt | @decl; + +@declparent = @file | @declstmt; + +@funcdef = @funclit | @funcdecl; + +@scopenode = @file | @functypeexpr | @blockstmt | @ifstmt | @caseclause | @switchstmt | @commclause | @loopstmt; + +@location = @location_default; + +@sourceline = @locatable; + +case @comment.kind of + 0 = @slashslashcomment +| 1 = @slashstarcomment; + +case @expr.kind of + 0 = @badexpr +| 1 = @ident +| 2 = @ellipsis +| 3 = @intlit +| 4 = @floatlit +| 5 = @imaglit +| 6 = @charlit +| 7 = @stringlit +| 8 = @funclit +| 9 = @compositelit +| 10 = @parenexpr +| 11 = @selectorexpr +| 12 = @indexexpr +| 13 = @sliceexpr +| 14 = @typeassertexpr +| 15 = @callorconversionexpr +| 16 = @starexpr +| 17 = @keyvalueexpr +| 18 = @arraytypeexpr +| 19 = @structtypeexpr +| 20 = @functypeexpr +| 21 = @interfacetypeexpr +| 22 = @maptypeexpr +| 23 = @plusexpr +| 24 = @minusexpr +| 25 = @notexpr +| 26 = @complementexpr +| 27 = @derefexpr +| 28 = @addressexpr +| 29 = @arrowexpr +| 30 = @lorexpr +| 31 = @landexpr +| 32 = @eqlexpr +| 33 = @neqexpr +| 34 = @lssexpr +| 35 = @leqexpr +| 36 = @gtrexpr +| 37 = @geqexpr +| 38 = @addexpr +| 39 = @subexpr +| 40 = @orexpr +| 41 = @xorexpr +| 42 = @mulexpr +| 43 = @quoexpr +| 44 = @remexpr +| 45 = @shlexpr +| 46 = @shrexpr +| 47 = @andexpr +| 48 = @andnotexpr +| 49 = @sendchantypeexpr +| 50 = @recvchantypeexpr +| 51 = @sendrcvchantypeexpr +| 52 = @errorexpr; + +@basiclit = @intlit | @floatlit | @imaglit | @charlit | @stringlit; + +@operatorexpr = @logicalexpr | @arithmeticexpr | @bitwiseexpr | @unaryexpr | @binaryexpr; + +@logicalexpr = @logicalunaryexpr | @logicalbinaryexpr; + +@arithmeticexpr = @arithmeticunaryexpr | @arithmeticbinaryexpr; + +@bitwiseexpr = @bitwiseunaryexpr | @bitwisebinaryexpr; + +@unaryexpr = @logicalunaryexpr | @bitwiseunaryexpr | @arithmeticunaryexpr | @derefexpr | @addressexpr | @arrowexpr; + +@logicalunaryexpr = @notexpr; + +@bitwiseunaryexpr = @complementexpr; + +@arithmeticunaryexpr = @plusexpr | @minusexpr; + +@binaryexpr = @logicalbinaryexpr | @bitwisebinaryexpr | @arithmeticbinaryexpr | @comparison; + +@logicalbinaryexpr = @lorexpr | @landexpr; + +@bitwisebinaryexpr = @shiftexpr | @orexpr | @xorexpr | @andexpr | @andnotexpr; + +@arithmeticbinaryexpr = @addexpr | @subexpr | @mulexpr | @quoexpr | @remexpr; + +@shiftexpr = @shlexpr | @shrexpr; + +@comparison = @equalitytest | @relationalcomparison; + +@equalitytest = @eqlexpr | @neqexpr; + +@relationalcomparison = @lssexpr | @leqexpr | @gtrexpr | @geqexpr; + +@chantypeexpr = @sendchantypeexpr | @recvchantypeexpr | @sendrcvchantypeexpr; + +case @stmt.kind of + 0 = @badstmt +| 1 = @declstmt +| 2 = @emptystmt +| 3 = @labeledstmt +| 4 = @exprstmt +| 5 = @sendstmt +| 6 = @incstmt +| 7 = @decstmt +| 8 = @gostmt +| 9 = @deferstmt +| 10 = @returnstmt +| 11 = @breakstmt +| 12 = @continuestmt +| 13 = @gotostmt +| 14 = @fallthroughstmt +| 15 = @blockstmt +| 16 = @ifstmt +| 17 = @caseclause +| 18 = @exprswitchstmt +| 19 = @typeswitchstmt +| 20 = @commclause +| 21 = @selectstmt +| 22 = @forstmt +| 23 = @rangestmt +| 24 = @assignstmt +| 25 = @definestmt +| 26 = @addassignstmt +| 27 = @subassignstmt +| 28 = @mulassignstmt +| 29 = @quoassignstmt +| 30 = @remassignstmt +| 31 = @andassignstmt +| 32 = @orassignstmt +| 33 = @xorassignstmt +| 34 = @shlassignstmt +| 35 = @shrassignstmt +| 36 = @andnotassignstmt; + +@incdecstmt = @incstmt | @decstmt; + +@assignment = @simpleassignstmt | @compoundassignstmt; + +@simpleassignstmt = @assignstmt | @definestmt; + +@compoundassignstmt = @addassignstmt | @subassignstmt | @mulassignstmt | @quoassignstmt | @remassignstmt + | @andassignstmt | @orassignstmt | @xorassignstmt | @shlassignstmt | @shrassignstmt | @andnotassignstmt; + +@branchstmt = @breakstmt | @continuestmt | @gotostmt | @fallthroughstmt; + +@switchstmt = @exprswitchstmt | @typeswitchstmt; + +@loopstmt = @forstmt | @rangestmt; + +case @decl.kind of + 0 = @baddecl +| 1 = @importdecl +| 2 = @constdecl +| 3 = @typedecl +| 4 = @vardecl +| 5 = @funcdecl; + +@gendecl = @importdecl | @constdecl | @typedecl | @vardecl; + +case @spec.kind of + 0 = @importspec +| 1 = @valuespec +| 2 = @typedefspec +| 3 = @aliasspec; + +@typespec = @typedefspec | @aliasspec; + +case @object.kind of + 0 = @pkgobject +| 1 = @decltypeobject +| 2 = @builtintypeobject +| 3 = @declconstobject +| 4 = @builtinconstobject +| 5 = @declvarobject +| 6 = @declfunctionobject +| 7 = @builtinfunctionobject +| 8 = @labelobject; + +@declobject = @decltypeobject | @declconstobject | @declvarobject | @declfunctionobject; + +@builtinobject = @builtintypeobject | @builtinconstobject | @builtinfunctionobject; + +@typeobject = @decltypeobject | @builtintypeobject; + +@valueobject = @constobject | @varobject | @functionobject; + +@constobject = @declconstobject | @builtinconstobject; + +@varobject = @declvarobject; + +@functionobject = @declfunctionobject | @builtinfunctionobject; + +case @scope.kind of + 0 = @universescope +| 1 = @packagescope +| 2 = @localscope; + +case @type.kind of + 0 = @invalidtype +| 1 = @boolexprtype +| 2 = @inttype +| 3 = @int8type +| 4 = @int16type +| 5 = @int32type +| 6 = @int64type +| 7 = @uinttype +| 8 = @uint8type +| 9 = @uint16type +| 10 = @uint32type +| 11 = @uint64type +| 12 = @uintptrtype +| 13 = @float32type +| 14 = @float64type +| 15 = @complex64type +| 16 = @complex128type +| 17 = @stringexprtype +| 18 = @unsafepointertype +| 19 = @boolliteraltype +| 20 = @intliteraltype +| 21 = @runeliteraltype +| 22 = @floatliteraltype +| 23 = @complexliteraltype +| 24 = @stringliteraltype +| 25 = @nilliteraltype +| 26 = @arraytype +| 27 = @slicetype +| 28 = @structtype +| 29 = @pointertype +| 30 = @interfacetype +| 31 = @tupletype +| 32 = @signaturetype +| 33 = @maptype +| 34 = @sendchantype +| 35 = @recvchantype +| 36 = @sendrcvchantype +| 37 = @namedtype; + +@basictype = @booltype | @numerictype | @stringtype | @literaltype | @invalidtype | @unsafepointertype; + +@booltype = @boolexprtype | @boolliteraltype; + +@numerictype = @integertype | @floattype | @complextype; + +@integertype = @signedintegertype | @unsignedintegertype; + +@signedintegertype = @inttype | @int8type | @int16type | @int32type | @int64type | @intliteraltype | @runeliteraltype; + +@unsignedintegertype = @uinttype | @uint8type | @uint16type | @uint32type | @uint64type | @uintptrtype; + +@floattype = @float32type | @float64type | @floatliteraltype; + +@complextype = @complex64type | @complex128type | @complexliteraltype; + +@stringtype = @stringexprtype | @stringliteraltype; + +@literaltype = @boolliteraltype | @intliteraltype | @runeliteraltype | @floatliteraltype | @complexliteraltype + | @stringliteraltype | @nilliteraltype; + +@compositetype = @containertype | @structtype | @pointertype | @interfacetype | @tupletype | @signaturetype | @namedtype; + +@containertype = @arraytype | @slicetype | @maptype | @chantype; + +@chantype = @sendchantype | @recvchantype | @sendrcvchantype; + +case @modexpr.kind of + 0 = @modcommentblock +| 1 = @modline +| 2 = @modlineblock +| 3 = @modlparen +| 4 = @modrparen; + +case @error.kind of + 0 = @unknownerror +| 1 = @listerror +| 2 = @parseerror +| 3 = @typeerror; + diff --git a/downgrades/qlpack.yml b/downgrades/qlpack.yml new file mode 100644 index 00000000000..d3e056bea64 --- /dev/null +++ b/downgrades/qlpack.yml @@ -0,0 +1,4 @@ +name: codeql/go-downgrades +groups: go +downgrades: . +library: true diff --git a/ql/lib/upgrades/8f168c8af3fee9b57bcfce85bb2ab708a5e3c828/exprs.ql b/ql/lib/upgrades/8f168c8af3fee9b57bcfce85bb2ab708a5e3c828/exprs.ql new file mode 100644 index 00000000000..2e62387de13 --- /dev/null +++ b/ql/lib/upgrades/8f168c8af3fee9b57bcfce85bb2ab708a5e3c828/exprs.ql @@ -0,0 +1,33 @@ +class Expr_ extends @expr { + string toString() { result = "Expr" } +} + +class ExprParent_ extends @exprparent { + string toString() { result = "ExprParent" } +} + +/** + * Two new kinds have been inserted such that `@sliceexpr` which used to have + * index 13 now has index 15. Another new kind has been inserted such that + * `@plusexpr` which used to have index 23 now has index 26. Entries with + * indices lower than 13 are unchanged. + */ +bindingset[old_index] +int new_index(int old_index) { + if old_index < 13 + then result = old_index + else + if old_index < 23 + then result = (15 - 13) + old_index + else result = (26 - 23) + old_index +} + +// The schema for exprs is: +// +// exprs(unique int id: @expr, +// int kind: int ref, +// int parent: @exprparent ref, +// int idx: int ref); +from Expr_ expr, int new_kind, ExprParent_ parent, int idx, int old_kind +where exprs(expr, old_kind, parent, idx) and new_kind = new_index(old_kind) +select expr, new_kind, parent, idx diff --git a/ql/lib/upgrades/8f168c8af3fee9b57bcfce85bb2ab708a5e3c828/go.dbscheme b/ql/lib/upgrades/8f168c8af3fee9b57bcfce85bb2ab708a5e3c828/go.dbscheme new file mode 100644 index 00000000000..90fa7836e0a --- /dev/null +++ b/ql/lib/upgrades/8f168c8af3fee9b57bcfce85bb2ab708a5e3c828/go.dbscheme @@ -0,0 +1,547 @@ +/** Auto-generated dbscheme; do not edit. */ + + +/** Duplicate code **/ + +duplicateCode( + unique int id : @duplication, + varchar(900) relativePath : string ref, + int equivClass : int ref); + +similarCode( + unique int id : @similarity, + varchar(900) relativePath : string ref, + int equivClass : int ref); + +@duplication_or_similarity = @duplication | @similarity; + +tokens( + int id : @duplication_or_similarity ref, + int offset : int ref, + int beginLine : int ref, + int beginColumn : int ref, + int endLine : int ref, + int endColumn : int ref); + +/** External data **/ + +externalData( + int id : @externalDataElement, + varchar(900) path : string ref, + int column: int ref, + varchar(900) value : string ref +); + +snapshotDate(unique date snapshotDate : date ref); + +sourceLocationPrefix(varchar(900) prefix : string ref); + + +/* + * XML Files + */ + +xmlEncoding( + unique int id: @file ref, + string encoding: string ref +); + +xmlDTDs( + unique int id: @xmldtd, + string root: string ref, + string publicId: string ref, + string systemId: string ref, + int fileid: @file ref +); + +xmlElements( + unique int id: @xmlelement, + string name: string ref, + int parentid: @xmlparent ref, + int idx: int ref, + int fileid: @file ref +); + +xmlAttrs( + unique int id: @xmlattribute, + int elementid: @xmlelement ref, + string name: string ref, + string value: string ref, + int idx: int ref, + int fileid: @file ref +); + +xmlNs( + int id: @xmlnamespace, + string prefixName: string ref, + string URI: string ref, + int fileid: @file ref +); + +xmlHasNs( + int elementId: @xmlnamespaceable ref, + int nsId: @xmlnamespace ref, + int fileid: @file ref +); + +xmlComments( + unique int id: @xmlcomment, + string text: string ref, + int parentid: @xmlparent ref, + int fileid: @file ref +); + +xmlChars( + unique int id: @xmlcharacters, + string text: string ref, + int parentid: @xmlparent ref, + int idx: int ref, + int isCDATA: int ref, + int fileid: @file ref +); + +@xmlparent = @file | @xmlelement; +@xmlnamespaceable = @xmlelement | @xmlattribute; + +xmllocations( + int xmlElement: @xmllocatable ref, + int location: @location_default ref +); + +@xmllocatable = @xmlcharacters | @xmlelement | @xmlcomment | @xmlattribute | @xmldtd | @file | @xmlnamespace; + +compilations(unique int id: @compilation, string cwd: string ref); + +#keyset[id, num] +compilation_args(int id: @compilation ref, int num: int ref, string arg: string ref); + +#keyset[id, num, kind] +compilation_time(int id: @compilation ref, int num: int ref, int kind: int ref, float secs: float ref); + +diagnostic_for(unique int diagnostic: @diagnostic ref, int compilation: @compilation ref, int file_number: int ref, int file_number_diagnostic_number: int ref); + +compilation_finished(unique int id: @compilation ref, float cpu_seconds: float ref, float elapsed_seconds: float ref); + +#keyset[id, num] +compilation_compiling_files(int id: @compilation ref, int num: int ref, int file: @file ref); + +diagnostics(unique int id: @diagnostic, int severity: int ref, string error_tag: string ref, string error_message: string ref, + string full_error_message: string ref, int location: @location ref); + +locations_default(unique int id: @location_default, int file: @file ref, int beginLine: int ref, int beginColumn: int ref, + int endLine: int ref, int endColumn: int ref); + +numlines(int element_id: @sourceline ref, int num_lines: int ref, int num_code: int ref, int num_comment: int ref); + +files(unique int id: @file, string name: string ref); + +folders(unique int id: @folder, string name: string ref); + +containerparent(int parent: @container ref, unique int child: @container ref); + +has_location(unique int locatable: @locatable ref, int location: @location ref); + +#keyset[parent, idx] +comment_groups(unique int id: @comment_group, int parent: @file ref, int idx: int ref); + +comments(unique int id: @comment, int kind: int ref, int parent: @comment_group ref, int idx: int ref, string text: string ref); + +doc_comments(unique int node: @documentable ref, int comment: @comment_group ref); + +#keyset[parent, idx] +exprs(unique int id: @expr, int kind: int ref, int parent: @exprparent ref, int idx: int ref); + +literals(unique int expr: @expr ref, string value: string ref, string raw: string ref); + +constvalues(unique int expr: @expr ref, string value: string ref, string exact: string ref); + +fields(unique int id: @field, int parent: @fieldparent ref, int idx: int ref); + +typeparamdecls(unique int id: @typeparamdecl, int parent: @typeparamdeclparent ref, int idx: int ref); + +#keyset[parent, idx] +stmts(unique int id: @stmt, int kind: int ref, int parent: @stmtparent ref, int idx: int ref); + +#keyset[parent, idx] +decls(unique int id: @decl, int kind: int ref, int parent: @declparent ref, int idx: int ref); + +#keyset[parent, idx] +specs(unique int id: @spec, int kind: int ref, int parent: @gendecl ref, int idx: int ref); + +scopes(unique int id: @scope, int kind: int ref); + +scopenesting(unique int inner: @scope ref, int outer: @scope ref); + +scopenodes(unique int node: @scopenode ref, int scope: @localscope ref); + +objects(unique int id: @object, int kind: int ref, string name: string ref); + +objectscopes(unique int object: @object ref, int scope: @scope ref); + +objecttypes(unique int object: @object ref, int tp: @type ref); + +methodreceivers(unique int method: @object ref, int receiver: @object ref); + +fieldstructs(unique int field: @object ref, int struct: @structtype ref); + +methodhosts(int method: @object ref, int host: @namedtype ref); + +defs(int ident: @ident ref, int object: @object ref); + +uses(int ident: @ident ref, int object: @object ref); + +types(unique int id: @type, int kind: int ref); + +type_of(unique int expr: @expr ref, int tp: @type ref); + +typename(unique int tp: @type ref, string name: string ref); + +key_type(unique int map: @maptype ref, int tp: @type ref); + +element_type(unique int container: @containertype ref, int tp: @type ref); + +base_type(unique int ptr: @pointertype ref, int tp: @type ref); + +underlying_type(unique int named: @namedtype ref, int tp: @type ref); + +#keyset[parent, index] +component_types(int parent: @compositetype ref, int index: int ref, string name: string ref, int tp: @type ref); + +array_length(unique int tp: @arraytype ref, string len: string ref); + +type_objects(unique int tp: @type ref, int object: @object ref); + +packages(unique int id: @package, string name: string ref, string path: string ref, int scope: @packagescope ref); + +#keyset[parent, idx] +modexprs(unique int id: @modexpr, int kind: int ref, int parent: @modexprparent ref, int idx: int ref); + +#keyset[parent, idx] +modtokens(string token: string ref, int parent: @modexpr ref, int idx: int ref); + +#keyset[package, idx] +errors(unique int id: @error, int kind: int ref, string msg: string ref, string rawpos: string ref, + string file: string ref, int line: int ref, int col: int ref, int package: @package ref, int idx: int ref); + +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); + +@container = @file | @folder; + +@locatable = @xmllocatable | @node | @localscope; + +@node = @documentable | @exprparent | @modexprparent | @fieldparent | @stmtparent | @declparent | @typeparamdeclparent + | @scopenode | @comment_group | @comment; + +@documentable = @file | @field | @typeparamdecl | @spec | @gendecl | @funcdecl | @modexpr; + +@exprparent = @funcdef | @file | @expr | @field | @stmt | @decl | @typeparamdecl | @spec; + +@modexprparent = @file | @modexpr; + +@fieldparent = @decl | @structtypeexpr | @functypeexpr | @interfacetypeexpr; + +@stmtparent = @funcdef | @stmt | @decl; + +@declparent = @file | @declstmt; + +@typeparamdeclparent = @funcdecl | @typespec; + +@funcdef = @funclit | @funcdecl; + +@scopenode = @file | @functypeexpr | @blockstmt | @ifstmt | @caseclause | @switchstmt | @commclause | @loopstmt; + +@location = @location_default; + +@sourceline = @locatable; + +case @comment.kind of + 0 = @slashslashcomment +| 1 = @slashstarcomment; + +case @expr.kind of + 0 = @badexpr +| 1 = @ident +| 2 = @ellipsis +| 3 = @intlit +| 4 = @floatlit +| 5 = @imaglit +| 6 = @charlit +| 7 = @stringlit +| 8 = @funclit +| 9 = @compositelit +| 10 = @parenexpr +| 11 = @selectorexpr +| 12 = @indexexpr +| 13 = @genericfunctioninstantiationexpr +| 14 = @generictypeinstantiationexpr +| 15 = @sliceexpr +| 16 = @typeassertexpr +| 17 = @callorconversionexpr +| 18 = @starexpr +| 19 = @keyvalueexpr +| 20 = @arraytypeexpr +| 21 = @structtypeexpr +| 22 = @functypeexpr +| 23 = @interfacetypeexpr +| 24 = @maptypeexpr +| 25 = @typesetliteralexpr +| 26 = @plusexpr +| 27 = @minusexpr +| 28 = @notexpr +| 29 = @complementexpr +| 30 = @derefexpr +| 31 = @addressexpr +| 32 = @arrowexpr +| 33 = @lorexpr +| 34 = @landexpr +| 35 = @eqlexpr +| 36 = @neqexpr +| 37 = @lssexpr +| 38 = @leqexpr +| 39 = @gtrexpr +| 40 = @geqexpr +| 41 = @addexpr +| 42 = @subexpr +| 43 = @orexpr +| 44 = @xorexpr +| 45 = @mulexpr +| 46 = @quoexpr +| 47 = @remexpr +| 48 = @shlexpr +| 49 = @shrexpr +| 50 = @andexpr +| 51 = @andnotexpr +| 52 = @sendchantypeexpr +| 53 = @recvchantypeexpr +| 54 = @sendrcvchantypeexpr +| 55 = @errorexpr; + +@basiclit = @intlit | @floatlit | @imaglit | @charlit | @stringlit; + +@operatorexpr = @logicalexpr | @arithmeticexpr | @bitwiseexpr | @unaryexpr | @binaryexpr; + +@logicalexpr = @logicalunaryexpr | @logicalbinaryexpr; + +@arithmeticexpr = @arithmeticunaryexpr | @arithmeticbinaryexpr; + +@bitwiseexpr = @bitwiseunaryexpr | @bitwisebinaryexpr; + +@unaryexpr = @logicalunaryexpr | @bitwiseunaryexpr | @arithmeticunaryexpr | @derefexpr | @addressexpr | @arrowexpr; + +@logicalunaryexpr = @notexpr; + +@bitwiseunaryexpr = @complementexpr; + +@arithmeticunaryexpr = @plusexpr | @minusexpr; + +@binaryexpr = @logicalbinaryexpr | @bitwisebinaryexpr | @arithmeticbinaryexpr | @comparison; + +@logicalbinaryexpr = @lorexpr | @landexpr; + +@bitwisebinaryexpr = @shiftexpr | @orexpr | @xorexpr | @andexpr | @andnotexpr; + +@arithmeticbinaryexpr = @addexpr | @subexpr | @mulexpr | @quoexpr | @remexpr; + +@shiftexpr = @shlexpr | @shrexpr; + +@comparison = @equalitytest | @relationalcomparison; + +@equalitytest = @eqlexpr | @neqexpr; + +@relationalcomparison = @lssexpr | @leqexpr | @gtrexpr | @geqexpr; + +@chantypeexpr = @sendchantypeexpr | @recvchantypeexpr | @sendrcvchantypeexpr; + +case @stmt.kind of + 0 = @badstmt +| 1 = @declstmt +| 2 = @emptystmt +| 3 = @labeledstmt +| 4 = @exprstmt +| 5 = @sendstmt +| 6 = @incstmt +| 7 = @decstmt +| 8 = @gostmt +| 9 = @deferstmt +| 10 = @returnstmt +| 11 = @breakstmt +| 12 = @continuestmt +| 13 = @gotostmt +| 14 = @fallthroughstmt +| 15 = @blockstmt +| 16 = @ifstmt +| 17 = @caseclause +| 18 = @exprswitchstmt +| 19 = @typeswitchstmt +| 20 = @commclause +| 21 = @selectstmt +| 22 = @forstmt +| 23 = @rangestmt +| 24 = @assignstmt +| 25 = @definestmt +| 26 = @addassignstmt +| 27 = @subassignstmt +| 28 = @mulassignstmt +| 29 = @quoassignstmt +| 30 = @remassignstmt +| 31 = @andassignstmt +| 32 = @orassignstmt +| 33 = @xorassignstmt +| 34 = @shlassignstmt +| 35 = @shrassignstmt +| 36 = @andnotassignstmt; + +@incdecstmt = @incstmt | @decstmt; + +@assignment = @simpleassignstmt | @compoundassignstmt; + +@simpleassignstmt = @assignstmt | @definestmt; + +@compoundassignstmt = @addassignstmt | @subassignstmt | @mulassignstmt | @quoassignstmt | @remassignstmt + | @andassignstmt | @orassignstmt | @xorassignstmt | @shlassignstmt | @shrassignstmt | @andnotassignstmt; + +@branchstmt = @breakstmt | @continuestmt | @gotostmt | @fallthroughstmt; + +@switchstmt = @exprswitchstmt | @typeswitchstmt; + +@loopstmt = @forstmt | @rangestmt; + +case @decl.kind of + 0 = @baddecl +| 1 = @importdecl +| 2 = @constdecl +| 3 = @typedecl +| 4 = @vardecl +| 5 = @funcdecl; + +@gendecl = @importdecl | @constdecl | @typedecl | @vardecl; + +case @spec.kind of + 0 = @importspec +| 1 = @valuespec +| 2 = @typedefspec +| 3 = @aliasspec; + +@typespec = @typedefspec | @aliasspec; + +case @object.kind of + 0 = @pkgobject +| 1 = @decltypeobject +| 2 = @builtintypeobject +| 3 = @declconstobject +| 4 = @builtinconstobject +| 5 = @declvarobject +| 6 = @declfunctionobject +| 7 = @builtinfunctionobject +| 8 = @labelobject; + +@typeparamparentobject = @decltypeobject | @declfunctionobject; + +@declobject = @decltypeobject | @declconstobject | @declvarobject | @declfunctionobject; + +@builtinobject = @builtintypeobject | @builtinconstobject | @builtinfunctionobject; + +@typeobject = @decltypeobject | @builtintypeobject; + +@valueobject = @constobject | @varobject | @functionobject; + +@constobject = @declconstobject | @builtinconstobject; + +@varobject = @declvarobject; + +@functionobject = @declfunctionobject | @builtinfunctionobject; + +case @scope.kind of + 0 = @universescope +| 1 = @packagescope +| 2 = @localscope; + +case @type.kind of + 0 = @invalidtype +| 1 = @boolexprtype +| 2 = @inttype +| 3 = @int8type +| 4 = @int16type +| 5 = @int32type +| 6 = @int64type +| 7 = @uinttype +| 8 = @uint8type +| 9 = @uint16type +| 10 = @uint32type +| 11 = @uint64type +| 12 = @uintptrtype +| 13 = @float32type +| 14 = @float64type +| 15 = @complex64type +| 16 = @complex128type +| 17 = @stringexprtype +| 18 = @unsafepointertype +| 19 = @boolliteraltype +| 20 = @intliteraltype +| 21 = @runeliteraltype +| 22 = @floatliteraltype +| 23 = @complexliteraltype +| 24 = @stringliteraltype +| 25 = @nilliteraltype +| 26 = @typeparamtype +| 27 = @arraytype +| 28 = @slicetype +| 29 = @structtype +| 30 = @pointertype +| 31 = @interfacetype +| 32 = @tupletype +| 33 = @signaturetype +| 34 = @maptype +| 35 = @sendchantype +| 36 = @recvchantype +| 37 = @sendrcvchantype +| 38 = @namedtype +| 39 = @typesetliteraltype; + +@basictype = @booltype | @numerictype | @stringtype | @literaltype | @invalidtype | @unsafepointertype; + +@booltype = @boolexprtype | @boolliteraltype; + +@numerictype = @integertype | @floattype | @complextype; + +@integertype = @signedintegertype | @unsignedintegertype; + +@signedintegertype = @inttype | @int8type | @int16type | @int32type | @int64type | @intliteraltype | @runeliteraltype; + +@unsignedintegertype = @uinttype | @uint8type | @uint16type | @uint32type | @uint64type | @uintptrtype; + +@floattype = @float32type | @float64type | @floatliteraltype; + +@complextype = @complex64type | @complex128type | @complexliteraltype; + +@stringtype = @stringexprtype | @stringliteraltype; + +@literaltype = @boolliteraltype | @intliteraltype | @runeliteraltype | @floatliteraltype | @complexliteraltype + | @stringliteraltype | @nilliteraltype; + +@compositetype = @typeparamtype | @containertype | @structtype | @pointertype | @interfacetype | @tupletype + | @signaturetype | @namedtype | @typesetliteraltype; + +@containertype = @arraytype | @slicetype | @maptype | @chantype; + +@chantype = @sendchantype | @recvchantype | @sendrcvchantype; + +case @modexpr.kind of + 0 = @modcommentblock +| 1 = @modline +| 2 = @modlineblock +| 3 = @modlparen +| 4 = @modrparen; + +case @error.kind of + 0 = @unknownerror +| 1 = @listerror +| 2 = @parseerror +| 3 = @typeerror; + diff --git a/ql/lib/upgrades/8f168c8af3fee9b57bcfce85bb2ab708a5e3c828/old.dbscheme b/ql/lib/upgrades/8f168c8af3fee9b57bcfce85bb2ab708a5e3c828/old.dbscheme new file mode 100644 index 00000000000..8f168c8af3f --- /dev/null +++ b/ql/lib/upgrades/8f168c8af3fee9b57bcfce85bb2ab708a5e3c828/old.dbscheme @@ -0,0 +1,531 @@ +/** Auto-generated dbscheme; do not edit. */ + + +/** Duplicate code **/ + +duplicateCode( + unique int id : @duplication, + varchar(900) relativePath : string ref, + int equivClass : int ref); + +similarCode( + unique int id : @similarity, + varchar(900) relativePath : string ref, + int equivClass : int ref); + +@duplication_or_similarity = @duplication | @similarity; + +tokens( + int id : @duplication_or_similarity ref, + int offset : int ref, + int beginLine : int ref, + int beginColumn : int ref, + int endLine : int ref, + int endColumn : int ref); + +/** External data **/ + +externalData( + int id : @externalDataElement, + varchar(900) path : string ref, + int column: int ref, + varchar(900) value : string ref +); + +snapshotDate(unique date snapshotDate : date ref); + +sourceLocationPrefix(varchar(900) prefix : string ref); + + +/* + * XML Files + */ + +xmlEncoding( + unique int id: @file ref, + string encoding: string ref +); + +xmlDTDs( + unique int id: @xmldtd, + string root: string ref, + string publicId: string ref, + string systemId: string ref, + int fileid: @file ref +); + +xmlElements( + unique int id: @xmlelement, + string name: string ref, + int parentid: @xmlparent ref, + int idx: int ref, + int fileid: @file ref +); + +xmlAttrs( + unique int id: @xmlattribute, + int elementid: @xmlelement ref, + string name: string ref, + string value: string ref, + int idx: int ref, + int fileid: @file ref +); + +xmlNs( + int id: @xmlnamespace, + string prefixName: string ref, + string URI: string ref, + int fileid: @file ref +); + +xmlHasNs( + int elementId: @xmlnamespaceable ref, + int nsId: @xmlnamespace ref, + int fileid: @file ref +); + +xmlComments( + unique int id: @xmlcomment, + string text: string ref, + int parentid: @xmlparent ref, + int fileid: @file ref +); + +xmlChars( + unique int id: @xmlcharacters, + string text: string ref, + int parentid: @xmlparent ref, + int idx: int ref, + int isCDATA: int ref, + int fileid: @file ref +); + +@xmlparent = @file | @xmlelement; +@xmlnamespaceable = @xmlelement | @xmlattribute; + +xmllocations( + int xmlElement: @xmllocatable ref, + int location: @location_default ref +); + +@xmllocatable = @xmlcharacters | @xmlelement | @xmlcomment | @xmlattribute | @xmldtd | @file | @xmlnamespace; + +compilations(unique int id: @compilation, string cwd: string ref); + +#keyset[id, num] +compilation_args(int id: @compilation ref, int num: int ref, string arg: string ref); + +#keyset[id, num, kind] +compilation_time(int id: @compilation ref, int num: int ref, int kind: int ref, float secs: float ref); + +diagnostic_for(unique int diagnostic: @diagnostic ref, int compilation: @compilation ref, int file_number: int ref, int file_number_diagnostic_number: int ref); + +compilation_finished(unique int id: @compilation ref, float cpu_seconds: float ref, float elapsed_seconds: float ref); + +#keyset[id, num] +compilation_compiling_files(int id: @compilation ref, int num: int ref, int file: @file ref); + +diagnostics(unique int id: @diagnostic, int severity: int ref, string error_tag: string ref, string error_message: string ref, + string full_error_message: string ref, int location: @location ref); + +locations_default(unique int id: @location_default, int file: @file ref, int beginLine: int ref, int beginColumn: int ref, + int endLine: int ref, int endColumn: int ref); + +numlines(int element_id: @sourceline ref, int num_lines: int ref, int num_code: int ref, int num_comment: int ref); + +files(unique int id: @file, string name: string ref); + +folders(unique int id: @folder, string name: string ref); + +containerparent(int parent: @container ref, unique int child: @container ref); + +has_location(unique int locatable: @locatable ref, int location: @location ref); + +#keyset[parent, idx] +comment_groups(unique int id: @comment_group, int parent: @file ref, int idx: int ref); + +comments(unique int id: @comment, int kind: int ref, int parent: @comment_group ref, int idx: int ref, string text: string ref); + +doc_comments(unique int node: @documentable ref, int comment: @comment_group ref); + +#keyset[parent, idx] +exprs(unique int id: @expr, int kind: int ref, int parent: @exprparent ref, int idx: int ref); + +literals(unique int expr: @expr ref, string value: string ref, string raw: string ref); + +constvalues(unique int expr: @expr ref, string value: string ref, string exact: string ref); + +fields(unique int id: @field, int parent: @fieldparent ref, int idx: int ref); + +#keyset[parent, idx] +stmts(unique int id: @stmt, int kind: int ref, int parent: @stmtparent ref, int idx: int ref); + +#keyset[parent, idx] +decls(unique int id: @decl, int kind: int ref, int parent: @declparent ref, int idx: int ref); + +#keyset[parent, idx] +specs(unique int id: @spec, int kind: int ref, int parent: @gendecl ref, int idx: int ref); + +scopes(unique int id: @scope, int kind: int ref); + +scopenesting(unique int inner: @scope ref, int outer: @scope ref); + +scopenodes(unique int node: @scopenode ref, int scope: @localscope ref); + +objects(unique int id: @object, int kind: int ref, string name: string ref); + +objectscopes(unique int object: @object ref, int scope: @scope ref); + +objecttypes(unique int object: @object ref, int tp: @type ref); + +methodreceivers(unique int method: @object ref, int receiver: @object ref); + +fieldstructs(unique int field: @object ref, int struct: @structtype ref); + +methodhosts(int method: @object ref, int host: @namedtype ref); + +defs(int ident: @ident ref, int object: @object ref); + +uses(int ident: @ident ref, int object: @object ref); + +types(unique int id: @type, int kind: int ref); + +type_of(unique int expr: @expr ref, int tp: @type ref); + +typename(unique int tp: @type ref, string name: string ref); + +key_type(unique int map: @maptype ref, int tp: @type ref); + +element_type(unique int container: @containertype ref, int tp: @type ref); + +base_type(unique int ptr: @pointertype ref, int tp: @type ref); + +underlying_type(unique int named: @namedtype ref, int tp: @type ref); + +#keyset[parent, index] +component_types(int parent: @compositetype ref, int index: int ref, string name: string ref, int tp: @type ref); + +array_length(unique int tp: @arraytype ref, string len: string ref); + +type_objects(unique int tp: @type ref, int object: @object ref); + +packages(unique int id: @package, string name: string ref, string path: string ref, int scope: @packagescope ref); + +#keyset[parent, idx] +modexprs(unique int id: @modexpr, int kind: int ref, int parent: @modexprparent ref, int idx: int ref); + +#keyset[parent, idx] +modtokens(string token: string ref, int parent: @modexpr ref, int idx: int ref); + +#keyset[package, idx] +errors(unique int id: @error, int kind: int ref, string msg: string ref, string rawpos: string ref, + string file: string ref, int line: int ref, int col: int ref, int package: @package ref, int idx: int ref); + +has_ellipsis(int id: @callorconversionexpr ref); + +variadic(int id: @signaturetype ref); + +@container = @file | @folder; + +@locatable = @xmllocatable | @node | @localscope; + +@node = @documentable | @exprparent | @modexprparent | @fieldparent | @stmtparent | @declparent | @scopenode + | @comment_group | @comment; + +@documentable = @file | @field | @spec | @gendecl | @funcdecl | @modexpr; + +@exprparent = @funcdef | @file | @expr | @field | @stmt | @decl | @spec; + +@modexprparent = @file | @modexpr; + +@fieldparent = @decl | @structtypeexpr | @functypeexpr | @interfacetypeexpr; + +@stmtparent = @funcdef | @stmt | @decl; + +@declparent = @file | @declstmt; + +@funcdef = @funclit | @funcdecl; + +@scopenode = @file | @functypeexpr | @blockstmt | @ifstmt | @caseclause | @switchstmt | @commclause | @loopstmt; + +@location = @location_default; + +@sourceline = @locatable; + +case @comment.kind of + 0 = @slashslashcomment +| 1 = @slashstarcomment; + +case @expr.kind of + 0 = @badexpr +| 1 = @ident +| 2 = @ellipsis +| 3 = @intlit +| 4 = @floatlit +| 5 = @imaglit +| 6 = @charlit +| 7 = @stringlit +| 8 = @funclit +| 9 = @compositelit +| 10 = @parenexpr +| 11 = @selectorexpr +| 12 = @indexexpr +| 13 = @sliceexpr +| 14 = @typeassertexpr +| 15 = @callorconversionexpr +| 16 = @starexpr +| 17 = @keyvalueexpr +| 18 = @arraytypeexpr +| 19 = @structtypeexpr +| 20 = @functypeexpr +| 21 = @interfacetypeexpr +| 22 = @maptypeexpr +| 23 = @plusexpr +| 24 = @minusexpr +| 25 = @notexpr +| 26 = @complementexpr +| 27 = @derefexpr +| 28 = @addressexpr +| 29 = @arrowexpr +| 30 = @lorexpr +| 31 = @landexpr +| 32 = @eqlexpr +| 33 = @neqexpr +| 34 = @lssexpr +| 35 = @leqexpr +| 36 = @gtrexpr +| 37 = @geqexpr +| 38 = @addexpr +| 39 = @subexpr +| 40 = @orexpr +| 41 = @xorexpr +| 42 = @mulexpr +| 43 = @quoexpr +| 44 = @remexpr +| 45 = @shlexpr +| 46 = @shrexpr +| 47 = @andexpr +| 48 = @andnotexpr +| 49 = @sendchantypeexpr +| 50 = @recvchantypeexpr +| 51 = @sendrcvchantypeexpr +| 52 = @errorexpr; + +@basiclit = @intlit | @floatlit | @imaglit | @charlit | @stringlit; + +@operatorexpr = @logicalexpr | @arithmeticexpr | @bitwiseexpr | @unaryexpr | @binaryexpr; + +@logicalexpr = @logicalunaryexpr | @logicalbinaryexpr; + +@arithmeticexpr = @arithmeticunaryexpr | @arithmeticbinaryexpr; + +@bitwiseexpr = @bitwiseunaryexpr | @bitwisebinaryexpr; + +@unaryexpr = @logicalunaryexpr | @bitwiseunaryexpr | @arithmeticunaryexpr | @derefexpr | @addressexpr | @arrowexpr; + +@logicalunaryexpr = @notexpr; + +@bitwiseunaryexpr = @complementexpr; + +@arithmeticunaryexpr = @plusexpr | @minusexpr; + +@binaryexpr = @logicalbinaryexpr | @bitwisebinaryexpr | @arithmeticbinaryexpr | @comparison; + +@logicalbinaryexpr = @lorexpr | @landexpr; + +@bitwisebinaryexpr = @shiftexpr | @orexpr | @xorexpr | @andexpr | @andnotexpr; + +@arithmeticbinaryexpr = @addexpr | @subexpr | @mulexpr | @quoexpr | @remexpr; + +@shiftexpr = @shlexpr | @shrexpr; + +@comparison = @equalitytest | @relationalcomparison; + +@equalitytest = @eqlexpr | @neqexpr; + +@relationalcomparison = @lssexpr | @leqexpr | @gtrexpr | @geqexpr; + +@chantypeexpr = @sendchantypeexpr | @recvchantypeexpr | @sendrcvchantypeexpr; + +case @stmt.kind of + 0 = @badstmt +| 1 = @declstmt +| 2 = @emptystmt +| 3 = @labeledstmt +| 4 = @exprstmt +| 5 = @sendstmt +| 6 = @incstmt +| 7 = @decstmt +| 8 = @gostmt +| 9 = @deferstmt +| 10 = @returnstmt +| 11 = @breakstmt +| 12 = @continuestmt +| 13 = @gotostmt +| 14 = @fallthroughstmt +| 15 = @blockstmt +| 16 = @ifstmt +| 17 = @caseclause +| 18 = @exprswitchstmt +| 19 = @typeswitchstmt +| 20 = @commclause +| 21 = @selectstmt +| 22 = @forstmt +| 23 = @rangestmt +| 24 = @assignstmt +| 25 = @definestmt +| 26 = @addassignstmt +| 27 = @subassignstmt +| 28 = @mulassignstmt +| 29 = @quoassignstmt +| 30 = @remassignstmt +| 31 = @andassignstmt +| 32 = @orassignstmt +| 33 = @xorassignstmt +| 34 = @shlassignstmt +| 35 = @shrassignstmt +| 36 = @andnotassignstmt; + +@incdecstmt = @incstmt | @decstmt; + +@assignment = @simpleassignstmt | @compoundassignstmt; + +@simpleassignstmt = @assignstmt | @definestmt; + +@compoundassignstmt = @addassignstmt | @subassignstmt | @mulassignstmt | @quoassignstmt | @remassignstmt + | @andassignstmt | @orassignstmt | @xorassignstmt | @shlassignstmt | @shrassignstmt | @andnotassignstmt; + +@branchstmt = @breakstmt | @continuestmt | @gotostmt | @fallthroughstmt; + +@switchstmt = @exprswitchstmt | @typeswitchstmt; + +@loopstmt = @forstmt | @rangestmt; + +case @decl.kind of + 0 = @baddecl +| 1 = @importdecl +| 2 = @constdecl +| 3 = @typedecl +| 4 = @vardecl +| 5 = @funcdecl; + +@gendecl = @importdecl | @constdecl | @typedecl | @vardecl; + +case @spec.kind of + 0 = @importspec +| 1 = @valuespec +| 2 = @typedefspec +| 3 = @aliasspec; + +@typespec = @typedefspec | @aliasspec; + +case @object.kind of + 0 = @pkgobject +| 1 = @decltypeobject +| 2 = @builtintypeobject +| 3 = @declconstobject +| 4 = @builtinconstobject +| 5 = @declvarobject +| 6 = @declfunctionobject +| 7 = @builtinfunctionobject +| 8 = @labelobject; + +@declobject = @decltypeobject | @declconstobject | @declvarobject | @declfunctionobject; + +@builtinobject = @builtintypeobject | @builtinconstobject | @builtinfunctionobject; + +@typeobject = @decltypeobject | @builtintypeobject; + +@valueobject = @constobject | @varobject | @functionobject; + +@constobject = @declconstobject | @builtinconstobject; + +@varobject = @declvarobject; + +@functionobject = @declfunctionobject | @builtinfunctionobject; + +case @scope.kind of + 0 = @universescope +| 1 = @packagescope +| 2 = @localscope; + +case @type.kind of + 0 = @invalidtype +| 1 = @boolexprtype +| 2 = @inttype +| 3 = @int8type +| 4 = @int16type +| 5 = @int32type +| 6 = @int64type +| 7 = @uinttype +| 8 = @uint8type +| 9 = @uint16type +| 10 = @uint32type +| 11 = @uint64type +| 12 = @uintptrtype +| 13 = @float32type +| 14 = @float64type +| 15 = @complex64type +| 16 = @complex128type +| 17 = @stringexprtype +| 18 = @unsafepointertype +| 19 = @boolliteraltype +| 20 = @intliteraltype +| 21 = @runeliteraltype +| 22 = @floatliteraltype +| 23 = @complexliteraltype +| 24 = @stringliteraltype +| 25 = @nilliteraltype +| 26 = @arraytype +| 27 = @slicetype +| 28 = @structtype +| 29 = @pointertype +| 30 = @interfacetype +| 31 = @tupletype +| 32 = @signaturetype +| 33 = @maptype +| 34 = @sendchantype +| 35 = @recvchantype +| 36 = @sendrcvchantype +| 37 = @namedtype; + +@basictype = @booltype | @numerictype | @stringtype | @literaltype | @invalidtype | @unsafepointertype; + +@booltype = @boolexprtype | @boolliteraltype; + +@numerictype = @integertype | @floattype | @complextype; + +@integertype = @signedintegertype | @unsignedintegertype; + +@signedintegertype = @inttype | @int8type | @int16type | @int32type | @int64type | @intliteraltype | @runeliteraltype; + +@unsignedintegertype = @uinttype | @uint8type | @uint16type | @uint32type | @uint64type | @uintptrtype; + +@floattype = @float32type | @float64type | @floatliteraltype; + +@complextype = @complex64type | @complex128type | @complexliteraltype; + +@stringtype = @stringexprtype | @stringliteraltype; + +@literaltype = @boolliteraltype | @intliteraltype | @runeliteraltype | @floatliteraltype | @complexliteraltype + | @stringliteraltype | @nilliteraltype; + +@compositetype = @containertype | @structtype | @pointertype | @interfacetype | @tupletype | @signaturetype | @namedtype; + +@containertype = @arraytype | @slicetype | @maptype | @chantype; + +@chantype = @sendchantype | @recvchantype | @sendrcvchantype; + +case @modexpr.kind of + 0 = @modcommentblock +| 1 = @modline +| 2 = @modlineblock +| 3 = @modlparen +| 4 = @modrparen; + +case @error.kind of + 0 = @unknownerror +| 1 = @listerror +| 2 = @parseerror +| 3 = @typeerror; + diff --git a/ql/lib/upgrades/8f168c8af3fee9b57bcfce85bb2ab708a5e3c828/types.ql b/ql/lib/upgrades/8f168c8af3fee9b57bcfce85bb2ab708a5e3c828/types.ql new file mode 100644 index 00000000000..7efff59c19a --- /dev/null +++ b/ql/lib/upgrades/8f168c8af3fee9b57bcfce85bb2ab708a5e3c828/types.ql @@ -0,0 +1,21 @@ +class Type_ extends @type { + string toString() { result = "Type" } +} + +/** + * A new kind has been inserted such that `@arraytype` which used to have index + * 26 now has index 27. Another new kind has been inserted at 39, which is the + * end of the list. Entries with lower indices are unchanged. + */ +bindingset[old_index] +int new_index(int old_index) { + if old_index < 26 then result = old_index else result = (27 - 26) + old_index +} + +// The schema for types is: +// +// types(unique int id: @type, +// int kind: int ref); +from Type_ type, int new_kind, int old_kind +where types(type, old_kind) and new_kind = new_index(old_kind) +select type, new_kind diff --git a/ql/lib/upgrades/8f168c8af3fee9b57bcfce85bb2ab708a5e3c828/upgrade.properties b/ql/lib/upgrades/8f168c8af3fee9b57bcfce85bb2ab708a5e3c828/upgrade.properties new file mode 100644 index 00000000000..51a005f8839 --- /dev/null +++ b/ql/lib/upgrades/8f168c8af3fee9b57bcfce85bb2ab708a5e3c828/upgrade.properties @@ -0,0 +1,4 @@ +description: Add generic instantiation expressions and type parameter types +compatibility: backwards +exprs.rel: run exprs.qlo +types.rel: run types.qlo From 1da53996521d1764899bb7e3584d1f4de783bfca Mon Sep 17 00:00:00 2001 From: Owen Mansel-Chan Date: Fri, 1 Apr 2022 17:03:17 +0100 Subject: [PATCH 16/49] Fix obvious test failures --- .../go/frameworks/Beego/ReflectedXss.expected | 6 +++--- .../go/frameworks/BeegoOrm/StoredXss.expected | 18 +++++++++--------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/ql/test/library-tests/semmle/go/frameworks/Beego/ReflectedXss.expected b/ql/test/library-tests/semmle/go/frameworks/Beego/ReflectedXss.expected index 05af1e46d56..2de1ce81fff 100644 --- a/ql/test/library-tests/semmle/go/frameworks/Beego/ReflectedXss.expected +++ b/ql/test/library-tests/semmle/go/frameworks/Beego/ReflectedXss.expected @@ -8,7 +8,7 @@ edges | test.go:30:20:30:26 | selection of c : subBindMe | test.go:30:13:30:29 | type conversion | | test.go:35:20:35:42 | call to Cookie : string | test.go:35:13:35:43 | type conversion | | test.go:40:20:40:31 | call to Data : map type | test.go:40:13:40:52 | type conversion | -| test.go:45:20:45:43 | call to GetData : interface type | test.go:45:13:45:53 | type conversion | +| test.go:45:20:45:43 | call to GetData : basic interface type | test.go:45:13:45:53 | type conversion | | test.go:50:20:50:42 | call to Header : string | test.go:50:13:50:43 | type conversion | | test.go:55:20:55:41 | call to Param : string | test.go:55:13:55:42 | type conversion | | test.go:60:20:60:33 | call to Params : map type | test.go:60:13:60:45 | type conversion | @@ -153,7 +153,7 @@ nodes | test.go:40:13:40:52 | type conversion | semmle.label | type conversion | | test.go:40:20:40:31 | call to Data : map type | semmle.label | call to Data : map type | | test.go:45:13:45:53 | type conversion | semmle.label | type conversion | -| test.go:45:20:45:43 | call to GetData : interface type | semmle.label | call to GetData : interface type | +| test.go:45:20:45:43 | call to GetData : basic interface type | semmle.label | call to GetData : basic interface type | | test.go:50:13:50:43 | type conversion | semmle.label | type conversion | | test.go:50:20:50:42 | call to Header : string | semmle.label | call to Header : string | | test.go:55:13:55:42 | type conversion | semmle.label | type conversion | @@ -267,7 +267,7 @@ subpaths | test.go:30:13:30:29 | type conversion | test.go:26:6:26:10 | definition of bound : bindMe | test.go:30:13:30:29 | type conversion | Cross-site scripting vulnerability due to $@. | test.go:26:6:26:10 | definition of bound | user-provided value | test.go:0:0:0:0 | test.go | | | test.go:35:13:35:43 | type conversion | test.go:35:20:35:42 | call to Cookie : string | test.go:35:13:35:43 | type conversion | Cross-site scripting vulnerability due to $@. | test.go:35:20:35:42 | call to Cookie | user-provided value | test.go:0:0:0:0 | test.go | | | test.go:40:13:40:52 | type conversion | test.go:40:20:40:31 | call to Data : map type | test.go:40:13:40:52 | type conversion | Cross-site scripting vulnerability due to $@. | test.go:40:20:40:31 | call to Data | user-provided value | test.go:0:0:0:0 | test.go | | -| test.go:45:13:45:53 | type conversion | test.go:45:20:45:43 | call to GetData : interface type | test.go:45:13:45:53 | type conversion | Cross-site scripting vulnerability due to $@. | test.go:45:20:45:43 | call to GetData | user-provided value | test.go:0:0:0:0 | test.go | | +| test.go:45:13:45:53 | type conversion | test.go:45:20:45:43 | call to GetData : basic interface type | test.go:45:13:45:53 | type conversion | Cross-site scripting vulnerability due to $@. | test.go:45:20:45:43 | call to GetData | user-provided value | test.go:0:0:0:0 | test.go | | | test.go:50:13:50:43 | type conversion | test.go:50:20:50:42 | call to Header : string | test.go:50:13:50:43 | type conversion | Cross-site scripting vulnerability due to $@. | test.go:50:20:50:42 | call to Header | user-provided value | test.go:0:0:0:0 | test.go | | | test.go:55:13:55:42 | type conversion | test.go:55:20:55:41 | call to Param : string | test.go:55:13:55:42 | type conversion | Cross-site scripting vulnerability due to $@. | test.go:55:20:55:41 | call to Param | user-provided value | test.go:0:0:0:0 | test.go | | | test.go:60:13:60:45 | type conversion | test.go:60:20:60:33 | call to Params : map type | test.go:60:13:60:45 | type conversion | Cross-site scripting vulnerability due to $@. | test.go:60:20:60:33 | call to Params | user-provided value | test.go:0:0:0:0 | test.go | | diff --git a/ql/test/library-tests/semmle/go/frameworks/BeegoOrm/StoredXss.expected b/ql/test/library-tests/semmle/go/frameworks/BeegoOrm/StoredXss.expected index dcd3a2f1029..e0d6b678335 100644 --- a/ql/test/library-tests/semmle/go/frameworks/BeegoOrm/StoredXss.expected +++ b/ql/test/library-tests/semmle/go/frameworks/BeegoOrm/StoredXss.expected @@ -9,13 +9,13 @@ edges | test.go:82:22:82:26 | &... : pointer type | test.go:83:13:83:30 | type conversion | | test.go:86:21:86:25 | &... : pointer type | test.go:87:13:87:30 | type conversion | | test.go:92:20:92:36 | call to Value : string | test.go:92:13:92:37 | type conversion | -| test.go:93:20:93:39 | call to RawValue : interface type | test.go:93:13:93:49 | type conversion | +| test.go:93:20:93:39 | call to RawValue : basic interface type | test.go:93:13:93:49 | type conversion | | test.go:94:20:94:37 | call to String : string | test.go:94:13:94:38 | type conversion | | test.go:95:20:95:36 | call to Value : string | test.go:95:13:95:37 | type conversion | -| test.go:96:20:96:39 | call to RawValue : interface type | test.go:96:13:96:49 | type conversion | +| test.go:96:20:96:39 | call to RawValue : basic interface type | test.go:96:13:96:49 | type conversion | | test.go:97:20:97:37 | call to String : string | test.go:97:13:97:38 | type conversion | | test.go:98:20:98:37 | call to Value : string | test.go:98:13:98:38 | type conversion | -| test.go:99:20:99:40 | call to RawValue : interface type | test.go:99:13:99:50 | type conversion | +| test.go:99:20:99:40 | call to RawValue : basic interface type | test.go:99:13:99:50 | type conversion | | test.go:100:20:100:38 | call to String : string | test.go:100:13:100:39 | type conversion | | test.go:106:9:106:13 | &... : pointer type | test.go:107:13:107:33 | type conversion | | test.go:106:9:106:13 | &... : pointer type | test.go:107:20:107:26 | implicit dereference : MyStruct | @@ -52,19 +52,19 @@ nodes | test.go:92:13:92:37 | type conversion | semmle.label | type conversion | | test.go:92:20:92:36 | call to Value : string | semmle.label | call to Value : string | | test.go:93:13:93:49 | type conversion | semmle.label | type conversion | -| test.go:93:20:93:39 | call to RawValue : interface type | semmle.label | call to RawValue : interface type | +| test.go:93:20:93:39 | call to RawValue : basic interface type | semmle.label | call to RawValue : basic interface type | | test.go:94:13:94:38 | type conversion | semmle.label | type conversion | | test.go:94:20:94:37 | call to String : string | semmle.label | call to String : string | | test.go:95:13:95:37 | type conversion | semmle.label | type conversion | | test.go:95:20:95:36 | call to Value : string | semmle.label | call to Value : string | | test.go:96:13:96:49 | type conversion | semmle.label | type conversion | -| test.go:96:20:96:39 | call to RawValue : interface type | semmle.label | call to RawValue : interface type | +| test.go:96:20:96:39 | call to RawValue : basic interface type | semmle.label | call to RawValue : basic interface type | | test.go:97:13:97:38 | type conversion | semmle.label | type conversion | | test.go:97:20:97:37 | call to String : string | semmle.label | call to String : string | | test.go:98:13:98:38 | type conversion | semmle.label | type conversion | | test.go:98:20:98:37 | call to Value : string | semmle.label | call to Value : string | | test.go:99:13:99:50 | type conversion | semmle.label | type conversion | -| test.go:99:20:99:40 | call to RawValue : interface type | semmle.label | call to RawValue : interface type | +| test.go:99:20:99:40 | call to RawValue : basic interface type | semmle.label | call to RawValue : basic interface type | | test.go:100:13:100:39 | type conversion | semmle.label | type conversion | | test.go:100:20:100:38 | call to String : string | semmle.label | call to String : string | | test.go:106:9:106:13 | &... : pointer type | semmle.label | &... : pointer type | @@ -104,13 +104,13 @@ subpaths | test.go:83:13:83:30 | type conversion | test.go:82:22:82:26 | &... : pointer type | test.go:83:13:83:30 | type conversion | Stored cross-site scripting vulnerability due to $@. | test.go:82:22:82:26 | &... | stored value | | test.go:87:13:87:30 | type conversion | test.go:86:21:86:25 | &... : pointer type | test.go:87:13:87:30 | type conversion | Stored cross-site scripting vulnerability due to $@. | test.go:86:21:86:25 | &... | stored value | | test.go:92:13:92:37 | type conversion | test.go:92:20:92:36 | call to Value : string | test.go:92:13:92:37 | type conversion | Stored cross-site scripting vulnerability due to $@. | test.go:92:20:92:36 | call to Value | stored value | -| test.go:93:13:93:49 | type conversion | test.go:93:20:93:39 | call to RawValue : interface type | test.go:93:13:93:49 | type conversion | Stored cross-site scripting vulnerability due to $@. | test.go:93:20:93:39 | call to RawValue | stored value | +| test.go:93:13:93:49 | type conversion | test.go:93:20:93:39 | call to RawValue : basic interface type | test.go:93:13:93:49 | type conversion | Stored cross-site scripting vulnerability due to $@. | test.go:93:20:93:39 | call to RawValue | stored value | | test.go:94:13:94:38 | type conversion | test.go:94:20:94:37 | call to String : string | test.go:94:13:94:38 | type conversion | Stored cross-site scripting vulnerability due to $@. | test.go:94:20:94:37 | call to String | stored value | | test.go:95:13:95:37 | type conversion | test.go:95:20:95:36 | call to Value : string | test.go:95:13:95:37 | type conversion | Stored cross-site scripting vulnerability due to $@. | test.go:95:20:95:36 | call to Value | stored value | -| test.go:96:13:96:49 | type conversion | test.go:96:20:96:39 | call to RawValue : interface type | test.go:96:13:96:49 | type conversion | Stored cross-site scripting vulnerability due to $@. | test.go:96:20:96:39 | call to RawValue | stored value | +| test.go:96:13:96:49 | type conversion | test.go:96:20:96:39 | call to RawValue : basic interface type | test.go:96:13:96:49 | type conversion | Stored cross-site scripting vulnerability due to $@. | test.go:96:20:96:39 | call to RawValue | stored value | | test.go:97:13:97:38 | type conversion | test.go:97:20:97:37 | call to String : string | test.go:97:13:97:38 | type conversion | Stored cross-site scripting vulnerability due to $@. | test.go:97:20:97:37 | call to String | stored value | | test.go:98:13:98:38 | type conversion | test.go:98:20:98:37 | call to Value : string | test.go:98:13:98:38 | type conversion | Stored cross-site scripting vulnerability due to $@. | test.go:98:20:98:37 | call to Value | stored value | -| test.go:99:13:99:50 | type conversion | test.go:99:20:99:40 | call to RawValue : interface type | test.go:99:13:99:50 | type conversion | Stored cross-site scripting vulnerability due to $@. | test.go:99:20:99:40 | call to RawValue | stored value | +| test.go:99:13:99:50 | type conversion | test.go:99:20:99:40 | call to RawValue : basic interface type | test.go:99:13:99:50 | type conversion | Stored cross-site scripting vulnerability due to $@. | test.go:99:20:99:40 | call to RawValue | stored value | | test.go:100:13:100:39 | type conversion | test.go:100:20:100:38 | call to String : string | test.go:100:13:100:39 | type conversion | Stored cross-site scripting vulnerability due to $@. | test.go:100:20:100:38 | call to String | stored value | | test.go:107:13:107:33 | type conversion | test.go:106:9:106:13 | &... : pointer type | test.go:107:13:107:33 | type conversion | Stored cross-site scripting vulnerability due to $@. | test.go:106:9:106:13 | &... | stored value | | test.go:111:13:111:29 | type conversion | test.go:110:9:110:12 | &... : pointer type | test.go:111:13:111:29 | type conversion | Stored cross-site scripting vulnerability due to $@. | test.go:110:9:110:12 | &... | stored value | From 4fa972fdc519d4981ed29a619b039e10b870fae9 Mon Sep 17 00:00:00 2001 From: Owen Mansel-Chan Date: Mon, 4 Apr 2022 11:45:58 +0100 Subject: [PATCH 17/49] Rename variable for clarity --- extractor/extractor.go | 4 ++-- extractor/trap/labels.go | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/extractor/extractor.go b/extractor/extractor.go index e8e15ed32c2..a7d09e9cfec 100644 --- a/extractor/extractor.go +++ b/extractor/extractor.go @@ -388,10 +388,10 @@ func extractMethod(tw *trap.Writer, meth *types.Func) trap.Label { // get the receiver type of the method recvtyp := meth.Type().(*types.Signature).Recv().Type() // ensure receiver type has been extracted - recvlbl := extractType(tw, recvtyp) + recvtyplbl := extractType(tw, recvtyp) // if the method label does not exist, extract it - methlbl, exists := tw.Labeler.MethodID(meth, recvlbl) + methlbl, exists := tw.Labeler.MethodID(meth, recvtyplbl) if !exists { populateTypeParamParents(tw, meth.Type().(*types.Signature).TypeParams(), methlbl) populateTypeParamParents(tw, meth.Type().(*types.Signature).RecvTypeParams(), methlbl) diff --git a/extractor/trap/labels.go b/extractor/trap/labels.go index 54420adb34d..1e9bd298447 100644 --- a/extractor/trap/labels.go +++ b/extractor/trap/labels.go @@ -220,12 +220,12 @@ func (l *Labeler) FieldID(field *types.Var, idx int, structlbl Label) (Label, bo } // MethodID associates a label with the given method and returns it, together with a flag indicating whether -// the method already had a label associated with it; the method must belong to `recvlbl`, since that label +// the method already had a label associated with it; the method must belong to `recvtyplbl`, since that label // is used to construct the label of the method -func (l *Labeler) MethodID(method types.Object, recvlbl Label) (Label, bool) { +func (l *Labeler) MethodID(method types.Object, recvtyplbl Label) (Label, bool) { label, exists := l.objectLabels[method] if !exists { - label = l.GlobalID(fmt.Sprintf("{%v},%s;method", recvlbl, method.Name())) + label = l.GlobalID(fmt.Sprintf("{%v},%s;method", recvtyplbl, method.Name())) l.objectLabels[method] = label } return label, exists From 4828430fd47862d4d2c75c841c92829b56f9f3b1 Mon Sep 17 00:00:00 2001 From: Owen Mansel-Chan Date: Mon, 4 Apr 2022 13:48:01 +0100 Subject: [PATCH 18/49] Extract all object types before emitting them Note that `extractObjectType` calls `extractType` which may add additional objects to the list that `ForEachObject` loops over, so we should emit object types as a second pass. --- extractor/extractor.go | 27 +++++++++++++++++++++++++-- extractor/trap/trapwriter.go | 19 +++++++++++++++---- 2 files changed, 40 insertions(+), 6 deletions(-) diff --git a/extractor/extractor.go b/extractor/extractor.go index a7d09e9cfec..ba6979d3d1f 100644 --- a/extractor/extractor.go +++ b/extractor/extractor.go @@ -138,7 +138,7 @@ func ExtractWithFlags(buildFlags []string, patterns []string) error { defer tw.Close() scope := extractPackageScope(tw, pkg) - tw.ForEachObject(extractObjectType) + extractObjectTypes(tw) lbl := tw.Labeler.GlobalID(util.EscapeTrapSpecialChars(pkg.PkgPath) + ";pkg") dbscheme.PackagesTable.Emit(tw, lbl, pkg.Name, pkg.PkgPath, scope) @@ -448,8 +448,31 @@ func extractObject(tw *trap.Writer, obj types.Object, lbl trap.Label) { } } +// extractObjectTypes extracts type and receiver information for all objects +func extractObjectTypes(tw *trap.Writer) { + // calling `extractType` on a named type will extract all methods defined + // on it, which will add new objects. Therefore we need to do this first + // before we loops over all objects and emit them. + changed := true + for changed { + changed = tw.ForEachObject(extractObjectType) + } + changed = tw.ForEachObject(emitObjectType) + if changed { + // TODO: Make this non-fatal before commiting + log.Fatalf("Warning: more objects were labeled while emitted object types") + } +} + // extractObjectType extracts type and receiver information for a given object func extractObjectType(tw *trap.Writer, obj types.Object, lbl trap.Label) { + if tp := obj.Type(); tp != nil { + extractType(tw, tp) + } +} + +// emitObjectType emits the type information for a given object +func emitObjectType(tw *trap.Writer, obj types.Object, lbl trap.Label) { if tp := obj.Type(); tp != nil { dbscheme.ObjectTypesTable.Emit(tw, lbl, extractType(tw, tp)) } @@ -596,7 +619,7 @@ func (extraction *Extraction) extractFile(ast *ast.File, pkg *packages.Package) extractFileNode(tw, ast) - tw.ForEachObject(extractObjectType) + extractObjectTypes(tw) extractNumLines(tw, path, ast) diff --git a/extractor/trap/trapwriter.go b/extractor/trap/trapwriter.go index 7647086b4dd..21a47078081 100644 --- a/extractor/trap/trapwriter.go +++ b/extractor/trap/trapwriter.go @@ -109,11 +109,22 @@ func (tw *Writer) Close() error { // ForEachObject iterates over all objects labeled by this labeler, and invokes // the provided callback with a writer for the trap file, the object, and its -// label. -func (tw *Writer) ForEachObject(cb func(*Writer, types.Object, Label)) { - for object, lbl := range tw.Labeler.objectLabels { - cb(tw, object, lbl) +// label. It returns true if any extra objects were labeled and false otherwise. +func (tw *Writer) ForEachObject(cb func(*Writer, types.Object, Label)) bool { + // copy the objects into an array so that our behaviour is deterministic even + // if `cb` adds any new objects + i := 0 + objects := make([]types.Object, len(tw.Labeler.objectLabels)) + for k := range tw.Labeler.objectLabels { + objects[i] = k + i++ } + + for _, object := range objects { + cb(tw, object, tw.Labeler.objectLabels[object]) + } + + return len(tw.Labeler.objectLabels) != len(objects) } const max_strlen = 1024 * 1024 From 9abc7ea61784784ad422e990d9fe49543854c437 Mon Sep 17 00:00:00 2001 From: Owen Mansel-Chan Date: Mon, 4 Apr 2022 14:36:22 +0100 Subject: [PATCH 19/49] Address review comments 1 --- extractor/dbscheme/tables.go | 9 ++++++--- extractor/extractor.go | 20 ++++++++++++++------ 2 files changed, 20 insertions(+), 9 deletions(-) diff --git a/extractor/dbscheme/tables.go b/extractor/dbscheme/tables.go index 00b3bef14b3..63332aa3b59 100644 --- a/extractor/dbscheme/tables.go +++ b/extractor/dbscheme/tables.go @@ -379,17 +379,20 @@ var ParenExpr = ExprKind.NewBranch("@parenexpr") var SelectorExpr = ExprKind.NewBranch("@selectorexpr") // IndexExpr is the type of AST nodes for index expressions and generic type -// instantiation expressions with one type argument +// instantiation expressions with one type argument. Note that syntactically +// unambiguous generic instantiations will be extracted as +// `GenericTypeInstantiationExpr`. var IndexExpr = ExprKind.NewBranch("@indexexpr") -// GenericFunctionInstantiationExpr is the type of AST nodes that represent a instantiation +// GenericFunctionInstantiationExpr is the type of AST nodes that represent an instantiation // of a generic type. These correspond to some index expression AST nodes and all index // list expression AST nodes. var GenericFunctionInstantiationExpr = ExprKind.NewBranch("@genericfunctioninstantiationexpr") // GenericTypeInstantiationExpr is the type of AST nodes that represent an instantiation // of a generic type. These correspond to some index expression AST nodes and all index -// list expression AST nodes. +// list expression AST nodes. Note some syntactically ambiguous instantations are +// extracted as an `IndexExpr` to be disambiguated in QL later. var GenericTypeInstantiationExpr = ExprKind.NewBranch("@generictypeinstantiationexpr") // SliceExpr is the type of slice expression AST nodes diff --git a/extractor/extractor.go b/extractor/extractor.go index ba6979d3d1f..94f32f2a574 100644 --- a/extractor/extractor.go +++ b/extractor/extractor.go @@ -363,10 +363,14 @@ func extractObjects(tw *trap.Writer, scope *types.Scope, scopeLabel trap.Label) obj := scope.Lookup(name) lbl, exists := tw.Labeler.ScopedObjectID(obj, func() trap.Label { return extractType(tw, obj.Type()) }) if !exists { + // Populate type parameter parents for functions. Note that methods + // 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(tw, funcObj.Type().(*types.Signature).TypeParams(), lbl) populateTypeParamParents(tw, funcObj.Type().(*types.Signature).RecvTypeParams(), lbl) } + // Populate type parameter parents for named types. if typeNameObj, ok := obj.(*types.TypeName); ok { if tp, ok := typeNameObj.Type().(*types.Named); ok { populateTypeParamParents(tw, tp.TypeParams(), lbl) @@ -393,6 +397,8 @@ func extractMethod(tw *trap.Writer, meth *types.Func) trap.Label { // if the method label does not exist, extract it methlbl, exists := tw.Labeler.MethodID(meth, recvtyplbl) 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(tw, meth.Type().(*types.Signature).TypeParams(), methlbl) populateTypeParamParents(tw, meth.Type().(*types.Signature).RecvTypeParams(), methlbl) extractObject(tw, meth, methlbl) @@ -994,8 +1000,7 @@ func extractExpr(tw *trap.Writer, expr ast.Expr, parent trap.Label, idx int) { _, isUnionType := typeOf(tw, expr).(*types.Union) if expr.Op == token.OR && isUnionType { kind = dbscheme.TypeSetLiteralExpr.Index() - n := flattenBinaryExprTree(tw, expr.X, lbl, 0) - flattenBinaryExprTree(tw, expr.Y, lbl, n) + flattenBinaryExprTree(tw, expr, lbl, 0) } else { tp := dbscheme.BinaryExprs[expr.Op] if tp == nil { @@ -1033,7 +1038,7 @@ func extractExpr(tw *trap.Writer, expr ast.Expr, parent trap.Label, idx int) { kind = dbscheme.InterfaceTypeExpr.Index() // expr.Methods contains methods, embedded interfaces and type set // literals. - overrideTypesOfTypeSetLiterals(tw, expr.Methods) + makeTypeSetLiteralsUnionTyped(tw, expr.Methods) extractFields(tw, expr.Methods, lbl, 0, 1) case *ast.MapType: if expr == nil { @@ -1813,8 +1818,9 @@ func createUnionFromType(t types.Type) *types.Union { // Go through a `FieldList` and update the types of all type set literals which // are not already union types to be union types. We do this by changing the -// types stored in `tw.Package.TypesInfo.Types`. -func overrideTypesOfTypeSetLiterals(tw *trap.Writer, fields *ast.FieldList) { +// types stored in `tw.Package.TypesInfo.Types`. Type set literals can only +// occur in two places: a type parameter declaration or a type in an interface. +func makeTypeSetLiteralsUnionTyped(tw *trap.Writer, fields *ast.FieldList) { if fields == nil || fields.List == nil { return } @@ -1853,7 +1859,9 @@ func extractTypeParamDecls(tw *trap.Writer, fields *ast.FieldList, parent trap.L return } - overrideTypesOfTypeSetLiterals(tw, fields) + // Type set literals can occur as the type in a type parameter declaration, + // so we ensure that they are union typed. + makeTypeSetLiteralsUnionTyped(tw, fields) idx := 0 for _, field := range fields.List { From 59aa7426ec6bd36ae13d4498acdbcddfeea62a00 Mon Sep 17 00:00:00 2001 From: Owen Mansel-Chan Date: Wed, 6 Apr 2022 22:49:24 +0100 Subject: [PATCH 20/49] Add comments about entities without a parent scope --- extractor/extractor.go | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/extractor/extractor.go b/extractor/extractor.go index 94f32f2a574..b99b54907f0 100644 --- a/extractor/extractor.go +++ b/extractor/extractor.go @@ -727,7 +727,8 @@ func extractScopeLocation(tw *trap.Writer, scope *types.Scope, lbl trap.Label) { } // extractScopes extracts symbol table information for the package scope and all local scopes -// of the given package +// of the given package. Note that this will not encounter methods or struct fields as +// they do not have a parent scope. func extractScopes(tw *trap.Writer, nd *ast.File, pkg *packages.Package) { pkgScopeLabel := extractPackageScope(tw, pkg) fileScope := pkg.TypesInfo.Scopes[nd] @@ -1457,7 +1458,9 @@ func extractType(tw *trap.Writer, tp types.Type) trap.Label { for i := 0; i < tp.NumFields(); i++ { field := tp.Field(i) - // ensure the field is associated with a label + // ensure the field is associated with a label - note that + // struct fields do not have a parent scope, so they are not + // dealt with by `extractScopes` fieldlbl, exists := tw.Labeler.FieldID(field, i, lbl) if !exists { extractObject(tw, field, fieldlbl) @@ -1479,6 +1482,8 @@ func extractType(tw *trap.Writer, tp types.Type) trap.Label { for i := 0; i < tp.NumMethods(); i++ { meth := tp.Method(i) + // Note that methods do not have a parent scope, so they are + // not dealt with by `extractScopes` extractMethod(tw, meth) extractComponentType(tw, lbl, i, meth.Name(), meth.Type()) @@ -1537,7 +1542,8 @@ func extractType(tw *trap.Writer, tp types.Type) trap.Label { dbscheme.TypeObjectTable.Emit(tw, lbl, entitylbl) } - // ensure all methods have labels + // ensure all methods have labels - note that methods do not have a + // parent scope, so they are not dealt with by `extractScopes` for i := 0; i < origintp.NumMethods(); i++ { meth := origintp.Method(i) From 253ca2bb67fd2df76a192aa6f48a8119ba309cf9 Mon Sep 17 00:00:00 2001 From: Owen Mansel-Chan Date: Thu, 7 Apr 2022 10:46:17 +0100 Subject: [PATCH 21/49] Address review comments 2 --- ql/lib/semmle/go/Types.qll | 79 +++++++++++++++++++------------------- 1 file changed, 40 insertions(+), 39 deletions(-) diff --git a/ql/lib/semmle/go/Types.qll b/ql/lib/semmle/go/Types.qll index ce1a5e8fa86..e4c664f66ef 100644 --- a/ql/lib/semmle/go/Types.qll +++ b/ql/lib/semmle/go/Types.qll @@ -83,7 +83,7 @@ class Type extends @type { private predicate implementsNotComparable(InterfaceType i) { ( forall(TypeSetLiteralType tslit | tslit = i.getAnEmbeddedTypeSetLiteral() | - tslit.hasInTypeSet(this) + tslit.includesType(this) ) and ( not i.hasMethod(_, _) @@ -112,22 +112,21 @@ class Type extends @type { or u instanceof ArrayType and u.(ArrayType).getElementType().implementsComparable() or - u instanceof InterfaceType and - ( - not u instanceof BasicInterfaceType and - if exists(u.(InterfaceType).getAnEmbeddedTypeSetLiteral()) + exists(InterfaceType uif | uif = u | + not uif instanceof BasicInterfaceType and + if exists(uif.getAnEmbeddedTypeSetLiteral()) then + // All types in the intersection of all the embedded type set + // literals must implement comparable. forall(Type intersectionType | - intersectionType = u.(InterfaceType).getAnEmbeddedTypeSetLiteral().getATerm().getType() and - forall(TypeSetLiteralType tslit | - tslit = u.(InterfaceType).getAnEmbeddedTypeSetLiteral() - | + intersectionType = uif.getAnEmbeddedTypeSetLiteral().getATerm().getType() and + forall(TypeSetLiteralType tslit | tslit = uif.getAnEmbeddedTypeSetLiteral() | intersectionType = tslit.getATerm().getType() ) | intersectionType.implementsComparable() ) - else u.(InterfaceType).isOrEmbedsComparable() + else uif.isOrEmbedsComparable() ) ) } @@ -610,8 +609,8 @@ class PointerType extends @pointertype, CompositeType { override string toString() { result = "pointer type" } } -private newtype TTerm = - MkTerm(TypeSetLiteralType tslit, int index) { component_types(tslit, index, _, _) } +private newtype TTypeSetTerm = + MkTypeSetTerm(TypeSetLiteralType tslit, int index) { component_types(tslit, index, _, _) } /** * A term in a type set literal. @@ -622,13 +621,13 @@ private newtype TTerm = * ~string * ``` */ -class Term extends TTerm { +class TypeSetTerm extends TTypeSetTerm { boolean tilde; Type tp; - Term() { + TypeSetTerm() { exists(TypeSetLiteralType tslit, int index | - this = MkTerm(tslit, index) and + this = MkTypeSetTerm(tslit, index) and ( component_types(tslit, index, "", tp) and tilde = false @@ -651,7 +650,7 @@ class Term extends TTerm { Type getType() { result = tp } /** Holds if `t` is in the type set of this term. */ - predicate hasInTypeSet(Type t) { if tilde = false then t = tp else t.getUnderlyingType() = tp } + predicate includesType(Type t) { if tilde = false then t = tp else t.getUnderlyingType() = tp } /** Gets a pretty-printed representation of this term. */ string pp() { @@ -661,15 +660,15 @@ class Term extends TTerm { } /** Gets a textual representation of this element. */ - string toString() { result = "term" } + string toString() { result = "type set term" } } -private Term getIntersection(Term term1, Term term2) { +private TypeSetTerm getIntersection(TypeSetTerm term1, TypeSetTerm term2) { term1.getType() = term2.getType() and if term1.hasTilde() then result = term2 else result = term1 } -Term getTermInIntersection(TypeSetLiteralType a, TypeSetLiteralType b) { +TypeSetTerm getTermInIntersection(TypeSetLiteralType a, TypeSetLiteralType b) { result = getIntersection(a.getATerm(), b.getATerm()) } @@ -689,13 +688,13 @@ Term getTermInIntersection(TypeSetLiteralType a, TypeSetLiteralType b) { */ class TypeSetLiteralType extends @typesetliteraltype, CompositeType { /** Gets the `i`th term in this type set literal. */ - Term getTerm(int i) { result = MkTerm(this, i) } + TypeSetTerm getTerm(int i) { result = MkTypeSetTerm(this, i) } /** Gets a term in this type set literal. */ - Term getATerm() { result = getTerm(_) } + TypeSetTerm getATerm() { result = getTerm(_) } /** Holds if `t` is in the type set of this type set literal. */ - predicate hasInTypeSet(Type t) { exists(int i | this.getTerm(i).hasInTypeSet(t)) } + predicate includesType(Type t) { this.getATerm().includesType(t) } /** * Gets the interface type specified by just this type set literal, if it @@ -705,15 +704,15 @@ class TypeSetLiteralType extends @typesetliteraltype, CompositeType { * the bound in a type parameter declaration. */ InterfaceType getInterfaceType() { - this = result.getExplicitlyEmbeddedTypeSetLiteral(0) and - not exists(result.getExplicitlyEmbeddedTypeSetLiteral(1)) and + this = result.getDirectlyEmbeddedTypeSetLiteral(0) and + not exists(result.getDirectlyEmbeddedTypeSetLiteral(1)) and not result.hasMethod(_, _) and - not exists(result.getAnExplicitlyEmbeddedInterface()) + not exists(result.getADirectlyEmbeddedInterface()) } language[monotonicAggregates] override string pp() { - result = concat(Term t, int i | t = this.getTerm(i) | t.pp(), " | " order by i) + result = concat(TypeSetTerm t, int i | t = this.getTerm(i) | t.pp(), " | " order by i) } override string toString() { result = "type set literal type" } @@ -723,30 +722,32 @@ class TypeSetLiteralType extends @typesetliteraltype, CompositeType { class InterfaceType extends @interfacetype, CompositeType { /** Gets the type of method `name` of this interface type. */ Type getMethodType(string name) { + // Note that negative indices correspond to embedded interfaces and type + // set literals. exists(int i | i >= 0 | component_types(this, i, name, result)) } override predicate hasMethod(string m, SignatureType t) { t = this.getMethodType(m) } /** - * Holds if `tp` is an explicitly embedded type with index `index`. + * Holds if `tp` is a directly embedded type with index `index`. * - * `tp` is either a type set literal type or its underlyign type is an + * `tp` (or its underlying type) is either a type set literal type or an * interface type. */ - private predicate hasExplicitlyEmbeddedType(int index, Type tp) { + private predicate hasDirectlyEmbeddedType(int index, Type tp) { index >= 0 and component_types(this, -(index + 1), _, tp) } /** - * Gets a type whose underlying type is an interface that is explicitly + * Gets a type whose underlying type is an interface that is directly * embedded into this interface. * * Note that the methods of the embedded interface are already considered * as part of the method set of this interface. */ - Type getAnExplicitlyEmbeddedInterface() { - hasExplicitlyEmbeddedType(_, result) and result.getUnderlyingType() instanceof InterfaceType + Type getADirectlyEmbeddedInterface() { + hasDirectlyEmbeddedType(_, result) and result.getUnderlyingType() instanceof InterfaceType } /** @@ -757,9 +758,9 @@ class InterfaceType extends @interfacetype, CompositeType { * as part of the method set of this interface. */ Type getAnEmbeddedInterface() { - result = this.getAnExplicitlyEmbeddedInterface() or + result = this.getADirectlyEmbeddedInterface() or result = - this.getAnExplicitlyEmbeddedInterface() + this.getADirectlyEmbeddedInterface() .getUnderlyingType() .(InterfaceType) .getAnEmbeddedInterface() @@ -778,10 +779,10 @@ class InterfaceType extends @interfacetype, CompositeType { * Gets the type set literal with index `index` from the definition of this * interface type. * - * Note that the indexing includes embedded interfaces but not methods. + * Note that the indexes are not contiguous. */ - TypeSetLiteralType getExplicitlyEmbeddedTypeSetLiteral(int index) { - hasExplicitlyEmbeddedType(index, result) + TypeSetLiteralType getDirectlyEmbeddedTypeSetLiteral(int index) { + hasDirectlyEmbeddedType(index, result) } /** @@ -790,9 +791,9 @@ class InterfaceType extends @interfacetype, CompositeType { * This includes type set literals of embedded interfaces. */ TypeSetLiteralType getAnEmbeddedTypeSetLiteral() { - result = this.getExplicitlyEmbeddedTypeSetLiteral(_) or + result = this.getDirectlyEmbeddedTypeSetLiteral(_) or result = - getAnExplicitlyEmbeddedInterface() + getADirectlyEmbeddedInterface() .getUnderlyingType() .(InterfaceType) .getAnEmbeddedTypeSetLiteral() From 8276ca04b44936db5683541e5ac053ad3d784d8f Mon Sep 17 00:00:00 2001 From: Owen Mansel-Chan Date: Thu, 7 Apr 2022 17:01:52 +0100 Subject: [PATCH 22/49] Use generic method not instantiated one in Uses --- extractor/extractor.go | 57 +++++++++++++++++++++++++++++++++++++++++- 1 file changed, 56 insertions(+), 1 deletion(-) diff --git a/extractor/extractor.go b/extractor/extractor.go index b99b54907f0..7a14e8769a1 100644 --- a/extractor/extractor.go +++ b/extractor/extractor.go @@ -844,7 +844,7 @@ func extractExpr(tw *trap.Writer, expr ast.Expr, parent trap.Label, idx int) { dbscheme.DefsTable.Emit(tw, lbl, objlbl) } } - use := tw.Package.TypesInfo.Uses[expr] + use := getObjectBeingUsed(tw, expr) if use != nil { useTyp := extractType(tw, use.Type()) objlbl, exists := tw.Labeler.LookupObjectID(use, useTyp) @@ -1893,3 +1893,58 @@ func populateTypeParamParents(tw *trap.Writer, typeparams *types.TypeParamList, } } } + +// getobjectBeingUsed looks up `ident` in `tw.Package.TypesInfo.Uses` and makes +// some changes to the object to avoid returning objects relating to instantiated +// types. +func getObjectBeingUsed(tw *trap.Writer, ident *ast.Ident) types.Object { + obj := tw.Package.TypesInfo.Uses[ident] + if obj == nil { + return nil + } + if funcObj, ok := obj.(*types.Func); ok { + sig := funcObj.Type().(*types.Signature) + if recv := sig.Recv(); recv != nil { + recvType := recv.Type() + originType, isSame := tryGetGenericType(recvType) + + if originType == nil { + if pointerType, ok := recvType.(*types.Pointer); ok { + originType, isSame = tryGetGenericType(pointerType.Elem()) + } + } + + if originType == nil || isSame { + return obj + } + + for i := 0; i < originType.NumMethods(); i++ { + meth := originType.Method(i) + if meth.Name() == funcObj.Name() { + return meth + } + } + if interfaceType, ok := originType.Underlying().(*types.Interface); ok { + for i := 0; i < interfaceType.NumMethods(); i++ { + meth := interfaceType.Method(i) + if meth.Name() == funcObj.Name() { + return meth + } + } + } + log.Fatalf("Could not find method %s on type %s", funcObj.Name(), originType) + } + } + + return obj +} + +// tryGetGenericType returns the generic type of `tp`, and a boolean indicating +// whether it is the same as `tp`. +func tryGetGenericType(tp types.Type) (*types.Named, bool) { + if namedType, ok := tp.(*types.Named); ok { + originType := namedType.Origin() + return originType, namedType == originType + } + return nil, false +} From 8c15199ca9d756c1d375836e934534ca2d04d1a2 Mon Sep 17 00:00:00 2001 From: Owen Mansel-Chan Date: Fri, 8 Apr 2022 12:12:46 +0100 Subject: [PATCH 23/49] Use generic struct field not instantiated one in Uses We do not extract instantiated named types, and instead use the generic type. But fields of the underlying struct of an instantiated named types are obtained from the Uses map. We solve this keeping track of which objects should be overridden by which other objects. --- extractor/extractor.go | 34 ++++++++++++++++++++++++++++++++++ extractor/trap/trapwriter.go | 2 ++ 2 files changed, 36 insertions(+) diff --git a/extractor/extractor.go b/extractor/extractor.go index 7a14e8769a1..f57bd2ebb6c 100644 --- a/extractor/extractor.go +++ b/extractor/extractor.go @@ -1531,6 +1531,7 @@ func extractType(tw *trap.Writer, tp types.Type) trap.Label { dbscheme.TypeNameTable.Emit(tw, lbl, origintp.Obj().Name()) underlying := origintp.Underlying() extractUnderlyingType(tw, lbl, underlying) + trackInstantiatedStructFields(tw, tp, origintp) entitylbl, exists := tw.Labeler.LookupObjectID(origintp.Obj(), lbl) if entitylbl == trap.InvalidLabel { @@ -1902,6 +1903,9 @@ func getObjectBeingUsed(tw *trap.Writer, ident *ast.Ident) types.Object { if obj == nil { return nil } + if override, ok := tw.ObjectsOverride[obj]; ok { + return override + } if funcObj, ok := obj.(*types.Func); ok { sig := funcObj.Type().(*types.Signature) if recv := sig.Recv(); recv != nil { @@ -1948,3 +1952,33 @@ func tryGetGenericType(tp types.Type) (*types.Named, bool) { } return nil, false } + +// trackInstantiatedStructFields tries to give the fields of an instantiated +// struct type underlying `tp` the same labels as the corresponding fields of +// the generic struct type. This is so that when we come across the +// instantiated field in `tw.Package.TypesInfo.Uses` we will get the label for +// the generic field instead. +func trackInstantiatedStructFields(tw *trap.Writer, tp, origintp *types.Named) { + if tp == origintp { + return + } + + if instantiatedStruct, ok := tp.Underlying().(*types.Struct); ok { + genericStruct, ok2 := origintp.Underlying().(*types.Struct) + if !ok2 { + log.Fatalf( + "Error: underlying type of instantiated type is a struct but underlying type of generic type is %s", + origintp.Underlying()) + } + + if instantiatedStruct.NumFields() != genericStruct.NumFields() { + log.Fatalf( + "Error: instantiated struct %s has different number of fields than the generic version %s (%d != %d)", + instantiatedStruct, genericStruct, instantiatedStruct.NumFields(), genericStruct.NumFields()) + } + + for i := 0; i < instantiatedStruct.NumFields(); i++ { + tw.ObjectsOverride[instantiatedStruct.Field(i)] = genericStruct.Field(i) + } + } +} diff --git a/extractor/trap/trapwriter.go b/extractor/trap/trapwriter.go index 21a47078081..713cb341827 100644 --- a/extractor/trap/trapwriter.go +++ b/extractor/trap/trapwriter.go @@ -27,6 +27,7 @@ type Writer struct { Package *packages.Package TypesOverride map[ast.Expr]types.Type TypeParamParent map[*types.TypeParam]Label + ObjectsOverride map[types.Object]types.Object } func FileFor(path string) (string, error) { @@ -66,6 +67,7 @@ func NewWriter(path string, pkg *packages.Package) (*Writer, error) { pkg, make(map[ast.Expr]types.Type), make(map[*types.TypeParam]Label), + make(map[types.Object]types.Object), } tw.Labeler = newLabeler(tw) return tw, nil From 25b91d8155bec78d5cf63388952814ce4ec7dd66 Mon Sep 17 00:00:00 2001 From: Owen Mansel-Chan Date: Fri, 8 Apr 2022 09:46:44 +0100 Subject: [PATCH 24/49] Update tests --- .../go/Types/GenericTypeInstantiationExpr.expected | 2 ++ .../go/Types/SignatureType_getNumParameter.expected | 2 ++ .../go/Types/SignatureType_getNumResult.expected | 2 ++ ql/test/library-tests/semmle/go/Types/generic.go | 12 ++++++++++++ 4 files changed, 18 insertions(+) diff --git a/ql/test/library-tests/semmle/go/Types/GenericTypeInstantiationExpr.expected b/ql/test/library-tests/semmle/go/Types/GenericTypeInstantiationExpr.expected index b73d611fac2..d22baa21da7 100644 --- a/ql/test/library-tests/semmle/go/Types/GenericTypeInstantiationExpr.expected +++ b/ql/test/library-tests/semmle/go/Types/GenericTypeInstantiationExpr.expected @@ -14,3 +14,5 @@ | generic.go:60:12:60:28 | generic type instantiation expression | generic.go:60:12:60:22 | GenericMap2 | 1 | generic.go:60:27:60:27 | U | | generic.go:61:12:61:28 | generic type instantiation expression | generic.go:61:12:61:25 | GenericChannel | 0 | generic.go:61:27:61:27 | U | | generic.go:62:12:62:26 | generic type instantiation expression | generic.go:62:12:62:23 | GenericNamed | 0 | generic.go:62:25:62:25 | U | +| generic.go:70:42:70:64 | generic type instantiation expression | generic.go:70:42:70:57 | GenericInterface | 0 | generic.go:70:59:70:63 | int32 | +| generic.go:74:41:74:62 | generic type instantiation expression | generic.go:74:41:74:54 | GenericStruct1 | 0 | generic.go:74:56:74:61 | string | diff --git a/ql/test/library-tests/semmle/go/Types/SignatureType_getNumParameter.expected b/ql/test/library-tests/semmle/go/Types/SignatureType_getNumParameter.expected index 3142d257275..843020b9a95 100644 --- a/ql/test/library-tests/semmle/go/Types/SignatureType_getNumParameter.expected +++ b/ql/test/library-tests/semmle/go/Types/SignatureType_getNumParameter.expected @@ -1,4 +1,6 @@ | depth.go:22:1:25:1 | function declaration | 0 | +| generic.go:70:1:72:1 | function declaration | 1 | +| generic.go:74:1:80:1 | function declaration | 1 | | main.go:5:1:5:30 | function declaration | 1 | | main.go:7:1:9:1 | function declaration | 2 | | main.go:11:1:11:14 | function declaration | 0 | diff --git a/ql/test/library-tests/semmle/go/Types/SignatureType_getNumResult.expected b/ql/test/library-tests/semmle/go/Types/SignatureType_getNumResult.expected index d09b6d212b3..9072be74806 100644 --- a/ql/test/library-tests/semmle/go/Types/SignatureType_getNumResult.expected +++ b/ql/test/library-tests/semmle/go/Types/SignatureType_getNumResult.expected @@ -1,4 +1,6 @@ | depth.go:22:1:25:1 | function declaration | 0 | +| generic.go:70:1:72:1 | function declaration | 1 | +| generic.go:74:1:80:1 | function declaration | 0 | | main.go:5:1:5:30 | function declaration | 0 | | main.go:7:1:9:1 | function declaration | 2 | | main.go:11:1:11:14 | function declaration | 0 | diff --git a/ql/test/library-tests/semmle/go/Types/generic.go b/ql/test/library-tests/semmle/go/Types/generic.go index 0a58c3d8f3e..70381bb2890 100644 --- a/ql/test/library-tests/semmle/go/Types/generic.go +++ b/ql/test/library-tests/semmle/go/Types/generic.go @@ -66,3 +66,15 @@ type MyInterface[U comparable] interface { type HasBlankTypeParam[_ any] struct{} type HasBlankTypeParams[_ any, _ comparable, T ~string] struct{} + +func callMethodOnInstantiatedInterface(x GenericInterface[int32]) int32 { + return x.GetT() +} + +func accessFieldsOfInstantiatedStruct(x GenericStruct1[string]) { + _ = x.valueField + _ = x.pointerField + _ = x.arrayField + _ = x.sliceField + _ = x.mapField +} From 4e71ab5cfc9868f637185a56a3e4978e0cc112df Mon Sep 17 00:00:00 2001 From: Owen Mansel-Chan Date: Fri, 8 Apr 2022 15:53:57 +0100 Subject: [PATCH 25/49] Update comment above first extraction of packages --- extractor/extractor.go | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/extractor/extractor.go b/extractor/extractor.go index f57bd2ebb6c..aafea417185 100644 --- a/extractor/extractor.go +++ b/extractor/extractor.go @@ -109,9 +109,7 @@ func ExtractWithFlags(buildFlags []string, patterns []string) error { // root directories of packages that we want to extract wantedRoots := make(map[string]bool) - // recursively visit all packages in depth-first order; - // on the way down, associate each package scope with its corresponding package, - // and on the way up extract the package's scope + // Do a post-order traversal and extract the package scope of each package packages.Visit(pkgs, func(pkg *packages.Package) bool { return true }, func(pkg *packages.Package) { From 7a7ca619b3ddcf5d00aa042b78779d00ee7275a1 Mon Sep 17 00:00:00 2001 From: Owen Mansel-Chan Date: Mon, 11 Apr 2022 12:55:34 +0100 Subject: [PATCH 26/49] Add data flow tests for generic structs, methods and functions --- .../GenericFunctionsAndTypes/Flows.expected | 0 .../GenericFunctionsAndTypes/Flows.ql | 2 + .../genericfunctions.go | 49 +++++++++++ .../generictypesandmethods.go | 88 +++++++++++++++++++ 4 files changed, 139 insertions(+) create mode 100644 ql/test/library-tests/semmle/go/dataflow/GenericFunctionsAndTypes/Flows.expected create mode 100644 ql/test/library-tests/semmle/go/dataflow/GenericFunctionsAndTypes/Flows.ql create mode 100644 ql/test/library-tests/semmle/go/dataflow/GenericFunctionsAndTypes/genericfunctions.go create mode 100644 ql/test/library-tests/semmle/go/dataflow/GenericFunctionsAndTypes/generictypesandmethods.go diff --git a/ql/test/library-tests/semmle/go/dataflow/GenericFunctionsAndTypes/Flows.expected b/ql/test/library-tests/semmle/go/dataflow/GenericFunctionsAndTypes/Flows.expected new file mode 100644 index 00000000000..e69de29bb2d diff --git a/ql/test/library-tests/semmle/go/dataflow/GenericFunctionsAndTypes/Flows.ql b/ql/test/library-tests/semmle/go/dataflow/GenericFunctionsAndTypes/Flows.ql new file mode 100644 index 00000000000..881d262f400 --- /dev/null +++ b/ql/test/library-tests/semmle/go/dataflow/GenericFunctionsAndTypes/Flows.ql @@ -0,0 +1,2 @@ +import go +import TestUtilities.InlineFlowTest diff --git a/ql/test/library-tests/semmle/go/dataflow/GenericFunctionsAndTypes/genericfunctions.go b/ql/test/library-tests/semmle/go/dataflow/GenericFunctionsAndTypes/genericfunctions.go new file mode 100644 index 00000000000..2ab18e235f8 --- /dev/null +++ b/ql/test/library-tests/semmle/go/dataflow/GenericFunctionsAndTypes/genericfunctions.go @@ -0,0 +1,49 @@ +package main + +func genericSource[T any](t T) string { + x := source() + return x +} + +func genericIdentity[T any](t T) T { + return t +} + +func genericSink1[T any](t T, u string) { + sink(u) // $ hasValueFlow="u" +} + +func genericSink2[T any](t T, u string) { + sink(u) // $ hasValueFlow="u" +} + +func test() { + var x struct { + x1 int + x2 string + } + { + s := genericSource(x) + sink(s) // $ hasValueFlow="s" + } + { + s := genericSource[int8](2) + sink(s) // $ hasValueFlow="s" + } + { + s := genericIdentity(source()) + sink(s) // $ hasValueFlow="s" + } + { + s := genericIdentity[string](source()) + sink(s) // $ hasValueFlow="s" + } + { + s := source() + genericSink1(x, s) + } + { + s := source() + genericSink2[uint16](3, s) + } +} diff --git a/ql/test/library-tests/semmle/go/dataflow/GenericFunctionsAndTypes/generictypesandmethods.go b/ql/test/library-tests/semmle/go/dataflow/GenericFunctionsAndTypes/generictypesandmethods.go new file mode 100644 index 00000000000..18d42d0e934 --- /dev/null +++ b/ql/test/library-tests/semmle/go/dataflow/GenericFunctionsAndTypes/generictypesandmethods.go @@ -0,0 +1,88 @@ +package main + +func source() string { + return "untrusted data" +} + +func sink(string) { +} + +type GenericStruct1[T any] struct { + Field T +} + +func (g GenericStruct1[U]) Identity(u U) U { return u } +func (g GenericStruct1[U]) Getter() U { return g.Field } +func (g GenericStruct1[U]) Setter(u U) { g.Field = u } + +type GenericStruct2[S, T any] struct { + Field1 S + Field2 T +} + +func (g GenericStruct2[U, _]) Identity1(u U) U { return u } +func (g GenericStruct2[U, _]) Getter1() U { return g.Field1 } +func (g GenericStruct2[U, _]) Setter1(u U) { g.Field1 = u } + +func (g GenericStruct2[_, V]) Identity2(v V) V { return v } +func (g GenericStruct2[_, V]) Getter2() V { return g.Field2 } +func (g GenericStruct2[_, V]) Setter2(v V) { g.Field2 = v } + +func main() { + { + gs1 := GenericStruct1[string]{source()} + sink(gs1.Field) // $ hasValueFlow="selection of Field" + } + { + gs1 := GenericStruct1[string]{""} + sink(gs1.Identity(source())) // $ hasValueFlow="call to Identity" + } + { + gs1 := GenericStruct1[string]{""} + gs1.Field = source() + sink(gs1.Getter()) // $ hasValueFlow="call to Getter" + } + { + gs1 := GenericStruct1[string]{""} + gs1.Setter(source()) + sink(gs1.Field) // $ hasValueFlow="selection of Field" + } + + { + gs2 := GenericStruct2[string, string]{source(), ""} + sink(gs2.Field1) // $ hasValueFlow="selection of Field1" + } + { + gs2 := GenericStruct2[string, string]{"", ""} + sink(gs2.Identity1(source())) // $ hasValueFlow="call to Identity1" + } + { + gs2 := GenericStruct2[string, string]{"", ""} + gs2.Field1 = source() + sink(gs2.Getter1()) // $ hasValueFlow="call to Getter1" + } + { + gs2 := GenericStruct2[string, string]{"", ""} + gs2.Setter1(source()) + sink(gs2.Field1) // $ hasValueFlow="selection of Field1" + } + + { + gs2 := GenericStruct2[string, string]{"", source()} + sink(gs2.Field2) // $ hasValueFlow="selection of Field2" + } + { + gs2 := GenericStruct2[string, string]{"", ""} + sink(gs2.Identity2(source())) // $ hasValueFlow="call to Identity2" + } + { + gs2 := GenericStruct2[string, string]{"", ""} + gs2.Field2 = source() + sink(gs2.Getter2()) // $ hasValueFlow="call to Getter2" + } + { + gs2 := GenericStruct2[string, string]{"", ""} + gs2.Setter2(source()) + sink(gs2.Field2) // $ hasValueFlow="selection of Field2" + } +} From ce9c9cfe9ddf08c24c2b81013b9dbdc3a67072f3 Mon Sep 17 00:00:00 2001 From: Owen Mansel-Chan Date: Mon, 11 Apr 2022 21:31:31 +0100 Subject: [PATCH 27/49] `CallExpr.getCalleeExpr` should get uninstantiated function --- ql/lib/semmle/go/Expr.qll | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/ql/lib/semmle/go/Expr.qll b/ql/lib/semmle/go/Expr.qll index 6268885f494..ded01894fac 100644 --- a/ql/lib/semmle/go/Expr.qll +++ b/ql/lib/semmle/go/Expr.qll @@ -839,7 +839,11 @@ class CallExpr extends CallOrConversionExpr { } /** Gets the expression representing the function being called. */ - Expr getCalleeExpr() { result = this.getChildExpr(0) } + Expr getCalleeExpr() { + if this.getChildExpr(0) instanceof GenericFunctionInstantiationExpr + then result = this.getChildExpr(0).(GenericFunctionInstantiationExpr).getBase() + else result = this.getChildExpr(0) + } /** Gets the `i`th argument expression of this call (0-based). */ Expr getArgument(int i) { @@ -866,16 +870,11 @@ class CallExpr extends CallOrConversionExpr { result = callee.(Ident).getName() or result = callee.(SelectorExpr).getSelector().getName() - or - result = callee.(GenericFunctionInstantiationExpr).getBase().(Ident).getName() ) } /** Gets the declared target of this call. */ - Function getTarget() { - this.getCalleeExpr() = result.getAReference() or - this.getCalleeExpr().(GenericFunctionInstantiationExpr).getBase() = result.getAReference() - } + Function getTarget() { this.getCalleeExpr() = result.getAReference() } /** Holds if this call has an ellipsis after its last argument. */ predicate hasEllipsis() { has_ellipsis(this) } From 26d4acd3b6b9ee7fb5c4c94b8e8c3f94f577a2ed Mon Sep 17 00:00:00 2001 From: Owen Mansel-Chan Date: Mon, 11 Apr 2022 21:32:18 +0100 Subject: [PATCH 28/49] generic function instantions aren't type exprs --- ql/lib/semmle/go/Expr.qll | 2 -- 1 file changed, 2 deletions(-) diff --git a/ql/lib/semmle/go/Expr.qll b/ql/lib/semmle/go/Expr.qll index ded01894fac..d32d87287fe 100644 --- a/ql/lib/semmle/go/Expr.qll +++ b/ql/lib/semmle/go/Expr.qll @@ -2061,8 +2061,6 @@ private predicate isTypeExprBottomUp(Expr e) { or e instanceof @typesetliteralexpr or - e instanceof @genericfunctioninstantiationexpr - or e instanceof @generictypeinstantiationexpr or e instanceof @indexexpr and isTypeExprBottomUp(e.getChildExpr(0)) From 5257c4ab450529a28f2f59be88c4309b4e447924 Mon Sep 17 00:00:00 2001 From: Owen Mansel-Chan Date: Tue, 12 Apr 2022 12:15:35 +0100 Subject: [PATCH 29/49] Add control flow test --- .../controlflow/ControlFlowGraph/CFG.expected | 105 ++++++++++++++++++ .../ControlFlowNode_getASuccessor.expected | 105 ++++++++++++++++++ .../controlflow/ControlFlowGraph/generic.go | 35 ++++++ 3 files changed, 245 insertions(+) create mode 100644 ql/test/library-tests/semmle/go/controlflow/ControlFlowGraph/generic.go diff --git a/ql/test/library-tests/semmle/go/controlflow/ControlFlowGraph/CFG.expected b/ql/test/library-tests/semmle/go/controlflow/ControlFlowGraph/CFG.expected index 4e62ba42deb..b97b385a402 100644 --- a/ql/test/library-tests/semmle/go/controlflow/ControlFlowGraph/CFG.expected +++ b/ql/test/library-tests/semmle/go/controlflow/ControlFlowGraph/CFG.expected @@ -496,6 +496,111 @@ edges | exprs.go:94:16:94:16 | (...) is false | exprs.go:94:21:94:21 | z | | exprs.go:94:16:94:16 | (...) is true | exprs.go:94:9:94:21 | ...\|\|... | | exprs.go:94:21:94:21 | z | exprs.go:94:9:94:21 | ...\|\|... | +| generic.go:0:0:0:0 | entry | generic.go:3:1:5:1 | skip | +| generic.go:3:1:5:1 | skip | generic.go:7:28:7:35 | skip | +| generic.go:7:1:7:1 | entry | generic.go:7:7:7:7 | argument corresponding to g | +| generic.go:7:1:7:55 | function declaration | generic.go:9:1:12:1 | skip | +| generic.go:7:7:7:7 | argument corresponding to g | generic.go:7:7:7:7 | initialization of g | +| generic.go:7:7:7:7 | initialization of g | generic.go:7:37:7:37 | argument corresponding to u | +| generic.go:7:28:7:35 | skip | generic.go:7:1:7:55 | function declaration | +| generic.go:7:37:7:37 | argument corresponding to u | generic.go:7:37:7:37 | initialization of u | +| generic.go:7:37:7:37 | initialization of u | generic.go:7:53:7:53 | u | +| generic.go:7:46:7:53 | return statement | generic.go:7:55:7:55 | exit | +| generic.go:7:53:7:53 | u | generic.go:7:46:7:53 | return statement | +| generic.go:9:1:12:1 | skip | generic.go:14:31:14:39 | skip | +| generic.go:14:1:14:1 | entry | generic.go:14:7:14:7 | argument corresponding to g | +| generic.go:14:1:14:59 | function declaration | generic.go:16:6:16:21 | skip | +| generic.go:14:7:14:7 | argument corresponding to g | generic.go:14:7:14:7 | initialization of g | +| generic.go:14:7:14:7 | initialization of g | generic.go:14:41:14:41 | argument corresponding to u | +| generic.go:14:31:14:39 | skip | generic.go:14:1:14:59 | function declaration | +| generic.go:14:41:14:41 | argument corresponding to u | generic.go:14:41:14:41 | initialization of u | +| generic.go:14:41:14:41 | initialization of u | generic.go:14:57:14:57 | u | +| generic.go:14:50:14:57 | return statement | generic.go:14:59:14:59 | exit | +| generic.go:14:57:14:57 | u | generic.go:14:50:14:57 | return statement | +| generic.go:16:1:16:1 | entry | generic.go:16:30:16:30 | argument corresponding to t | +| generic.go:16:1:18:1 | function declaration | generic.go:20:6:20:21 | skip | +| generic.go:16:6:16:21 | skip | generic.go:16:1:18:1 | function declaration | +| generic.go:16:30:16:30 | argument corresponding to t | generic.go:16:30:16:30 | initialization of t | +| generic.go:16:30:16:30 | initialization of t | generic.go:17:9:17:9 | t | +| generic.go:17:2:17:9 | return statement | generic.go:18:1:18:1 | exit | +| generic.go:17:9:17:9 | t | generic.go:17:2:17:9 | return statement | +| generic.go:20:1:20:1 | entry | generic.go:20:33:20:33 | argument corresponding to s | +| generic.go:20:1:22:1 | function declaration | generic.go:24:6:24:12 | skip | +| generic.go:20:6:20:21 | skip | generic.go:20:1:22:1 | function declaration | +| generic.go:20:33:20:33 | argument corresponding to s | generic.go:20:33:20:33 | initialization of s | +| generic.go:20:33:20:33 | initialization of s | generic.go:20:38:20:38 | argument corresponding to t | +| generic.go:20:38:20:38 | argument corresponding to t | generic.go:20:38:20:38 | initialization of t | +| generic.go:20:38:20:38 | initialization of t | generic.go:21:9:21:9 | s | +| generic.go:21:2:21:12 | return statement | generic.go:22:1:22:1 | exit | +| generic.go:21:9:21:9 | s | generic.go:21:12:21:12 | t | +| generic.go:21:12:21:12 | t | generic.go:21:2:21:12 | return statement | +| generic.go:24:1:24:1 | entry | generic.go:25:2:25:4 | skip | +| generic.go:24:1:35:1 | function declaration | generic.go:0:0:0:0 | exit | +| generic.go:24:6:24:12 | skip | generic.go:24:1:35:1 | function declaration | +| generic.go:25:2:25:4 | assignment to gs1 | generic.go:26:2:26:2 | skip | +| generic.go:25:2:25:4 | skip | generic.go:25:9:25:35 | struct literal | +| generic.go:25:9:25:35 | struct literal | generic.go:25:32:25:34 | "x" | +| generic.go:25:32:25:34 | "x" | generic.go:25:32:25:34 | init of "x" | +| generic.go:25:32:25:34 | init of "x" | generic.go:25:2:25:4 | assignment to gs1 | +| generic.go:26:2:26:2 | assignment to a | generic.go:27:2:27:4 | skip | +| generic.go:26:2:26:2 | skip | generic.go:26:7:26:9 | gs1 | +| generic.go:26:7:26:9 | gs1 | generic.go:26:7:26:18 | selection of Identity | +| generic.go:26:7:26:18 | selection of Identity | generic.go:26:20:26:26 | "hello" | +| generic.go:26:7:26:27 | call to Identity | generic.go:26:2:26:2 | assignment to a | +| generic.go:26:7:26:27 | call to Identity | generic.go:35:1:35:1 | exit | +| generic.go:26:20:26:26 | "hello" | generic.go:26:7:26:27 | call to Identity | +| generic.go:27:2:27:4 | assignment to gs2 | generic.go:28:2:28:2 | skip | +| generic.go:27:2:27:4 | skip | generic.go:27:9:27:48 | struct literal | +| generic.go:27:9:27:48 | struct literal | generic.go:27:40:27:42 | "y" | +| generic.go:27:40:27:42 | "y" | generic.go:27:40:27:42 | init of "y" | +| generic.go:27:40:27:42 | init of "y" | generic.go:27:45:27:47 | "z" | +| generic.go:27:45:27:47 | "z" | generic.go:27:45:27:47 | init of "z" | +| generic.go:27:45:27:47 | init of "z" | generic.go:27:2:27:4 | assignment to gs2 | +| generic.go:28:2:28:2 | assignment to b | generic.go:29:2:29:2 | skip | +| generic.go:28:2:28:2 | skip | generic.go:28:7:28:9 | gs2 | +| generic.go:28:7:28:9 | gs2 | generic.go:28:7:28:19 | selection of Identity1 | +| generic.go:28:7:28:19 | selection of Identity1 | generic.go:28:21:28:21 | a | +| generic.go:28:7:28:22 | call to Identity1 | generic.go:28:2:28:2 | assignment to b | +| generic.go:28:7:28:22 | call to Identity1 | generic.go:35:1:35:1 | exit | +| generic.go:28:21:28:21 | a | generic.go:28:7:28:22 | call to Identity1 | +| generic.go:29:2:29:2 | assignment to c | generic.go:30:2:30:2 | skip | +| generic.go:29:2:29:2 | skip | generic.go:29:7:29:22 | genericIdentity1 | +| generic.go:29:7:29:22 | genericIdentity1 | generic.go:29:32:29:32 | b | +| generic.go:29:7:29:33 | call to genericIdentity1 | generic.go:29:2:29:2 | assignment to c | +| generic.go:29:7:29:33 | call to genericIdentity1 | generic.go:35:1:35:1 | exit | +| generic.go:29:32:29:32 | b | generic.go:29:7:29:33 | call to genericIdentity1 | +| generic.go:30:2:30:2 | assignment to d | generic.go:31:2:31:2 | skip | +| generic.go:30:2:30:2 | skip | generic.go:30:7:30:22 | genericIdentity1 | +| generic.go:30:7:30:22 | genericIdentity1 | generic.go:30:24:30:24 | c | +| generic.go:30:7:30:25 | call to genericIdentity1 | generic.go:30:2:30:2 | assignment to d | +| generic.go:30:7:30:25 | call to genericIdentity1 | generic.go:35:1:35:1 | exit | +| generic.go:30:24:30:24 | c | generic.go:30:7:30:25 | call to genericIdentity1 | +| generic.go:31:2:31:2 | assignment to e | generic.go:31:2:31:53 | ... := ...[1] | +| generic.go:31:2:31:2 | skip | generic.go:31:5:31:5 | skip | +| generic.go:31:2:31:53 | ... := ...[0] | generic.go:31:2:31:2 | assignment to e | +| generic.go:31:2:31:53 | ... := ...[1] | generic.go:31:5:31:5 | assignment to f | +| generic.go:31:5:31:5 | assignment to f | generic.go:32:2:32:2 | skip | +| generic.go:31:5:31:5 | skip | generic.go:31:10:31:25 | genericIdentity2 | +| generic.go:31:10:31:25 | genericIdentity2 | generic.go:31:43:31:43 | d | +| generic.go:31:10:31:53 | call to genericIdentity2 | generic.go:31:2:31:53 | ... := ...[0] | +| generic.go:31:10:31:53 | call to genericIdentity2 | generic.go:35:1:35:1 | exit | +| generic.go:31:43:31:43 | d | generic.go:31:46:31:52 | "hello" | +| generic.go:31:46:31:52 | "hello" | generic.go:31:10:31:53 | call to genericIdentity2 | +| generic.go:32:2:32:2 | assignment to g | generic.go:32:2:32:31 | ... := ...[1] | +| generic.go:32:2:32:2 | skip | generic.go:32:5:32:5 | skip | +| generic.go:32:2:32:31 | ... := ...[0] | generic.go:32:2:32:2 | assignment to g | +| generic.go:32:2:32:31 | ... := ...[1] | generic.go:32:5:32:5 | assignment to h | +| generic.go:32:5:32:5 | assignment to h | generic.go:33:2:33:2 | skip | +| generic.go:32:5:32:5 | skip | generic.go:32:10:32:25 | genericIdentity2 | +| generic.go:32:10:32:25 | genericIdentity2 | generic.go:32:27:32:27 | e | +| generic.go:32:10:32:31 | call to genericIdentity2 | generic.go:32:2:32:31 | ... := ...[0] | +| generic.go:32:10:32:31 | call to genericIdentity2 | generic.go:35:1:35:1 | exit | +| generic.go:32:27:32:27 | e | generic.go:32:30:32:30 | f | +| generic.go:32:30:32:30 | f | generic.go:32:10:32:31 | call to genericIdentity2 | +| generic.go:33:2:33:2 | skip | generic.go:33:6:33:6 | g | +| generic.go:33:6:33:6 | g | generic.go:34:2:34:2 | skip | +| generic.go:34:2:34:2 | skip | generic.go:34:6:34:6 | h | +| generic.go:34:6:34:6 | h | generic.go:35:1:35:1 | exit | | hello.go:0:0:0:0 | entry | hello.go:3:1:3:12 | skip | | hello.go:3:1:3:12 | skip | hello.go:5:7:5:13 | skip | | hello.go:5:7:5:13 | assignment to message | hello.go:7:6:7:13 | skip | diff --git a/ql/test/library-tests/semmle/go/controlflow/ControlFlowGraph/ControlFlowNode_getASuccessor.expected b/ql/test/library-tests/semmle/go/controlflow/ControlFlowGraph/ControlFlowNode_getASuccessor.expected index c83ad2519b8..7daed37dfb3 100644 --- a/ql/test/library-tests/semmle/go/controlflow/ControlFlowGraph/ControlFlowNode_getASuccessor.expected +++ b/ql/test/library-tests/semmle/go/controlflow/ControlFlowGraph/ControlFlowNode_getASuccessor.expected @@ -495,6 +495,111 @@ | exprs.go:94:16:94:16 | (...) is false | exprs.go:94:21:94:21 | z | | exprs.go:94:16:94:16 | (...) is true | exprs.go:94:9:94:21 | ...\|\|... | | exprs.go:94:21:94:21 | z | exprs.go:94:9:94:21 | ...\|\|... | +| generic.go:0:0:0:0 | entry | generic.go:3:1:5:1 | skip | +| generic.go:3:1:5:1 | skip | generic.go:7:28:7:35 | skip | +| generic.go:7:1:7:1 | entry | generic.go:7:7:7:7 | argument corresponding to g | +| generic.go:7:1:7:55 | function declaration | generic.go:9:1:12:1 | skip | +| generic.go:7:7:7:7 | argument corresponding to g | generic.go:7:7:7:7 | initialization of g | +| generic.go:7:7:7:7 | initialization of g | generic.go:7:37:7:37 | argument corresponding to u | +| generic.go:7:28:7:35 | skip | generic.go:7:1:7:55 | function declaration | +| generic.go:7:37:7:37 | argument corresponding to u | generic.go:7:37:7:37 | initialization of u | +| generic.go:7:37:7:37 | initialization of u | generic.go:7:53:7:53 | u | +| generic.go:7:46:7:53 | return statement | generic.go:7:55:7:55 | exit | +| generic.go:7:53:7:53 | u | generic.go:7:46:7:53 | return statement | +| generic.go:9:1:12:1 | skip | generic.go:14:31:14:39 | skip | +| generic.go:14:1:14:1 | entry | generic.go:14:7:14:7 | argument corresponding to g | +| generic.go:14:1:14:59 | function declaration | generic.go:16:6:16:21 | skip | +| generic.go:14:7:14:7 | argument corresponding to g | generic.go:14:7:14:7 | initialization of g | +| generic.go:14:7:14:7 | initialization of g | generic.go:14:41:14:41 | argument corresponding to u | +| generic.go:14:31:14:39 | skip | generic.go:14:1:14:59 | function declaration | +| generic.go:14:41:14:41 | argument corresponding to u | generic.go:14:41:14:41 | initialization of u | +| generic.go:14:41:14:41 | initialization of u | generic.go:14:57:14:57 | u | +| generic.go:14:50:14:57 | return statement | generic.go:14:59:14:59 | exit | +| generic.go:14:57:14:57 | u | generic.go:14:50:14:57 | return statement | +| generic.go:16:1:16:1 | entry | generic.go:16:30:16:30 | argument corresponding to t | +| generic.go:16:1:18:1 | function declaration | generic.go:20:6:20:21 | skip | +| generic.go:16:6:16:21 | skip | generic.go:16:1:18:1 | function declaration | +| generic.go:16:30:16:30 | argument corresponding to t | generic.go:16:30:16:30 | initialization of t | +| generic.go:16:30:16:30 | initialization of t | generic.go:17:9:17:9 | t | +| generic.go:17:2:17:9 | return statement | generic.go:18:1:18:1 | exit | +| generic.go:17:9:17:9 | t | generic.go:17:2:17:9 | return statement | +| generic.go:20:1:20:1 | entry | generic.go:20:33:20:33 | argument corresponding to s | +| generic.go:20:1:22:1 | function declaration | generic.go:24:6:24:12 | skip | +| generic.go:20:6:20:21 | skip | generic.go:20:1:22:1 | function declaration | +| generic.go:20:33:20:33 | argument corresponding to s | generic.go:20:33:20:33 | initialization of s | +| generic.go:20:33:20:33 | initialization of s | generic.go:20:38:20:38 | argument corresponding to t | +| generic.go:20:38:20:38 | argument corresponding to t | generic.go:20:38:20:38 | initialization of t | +| generic.go:20:38:20:38 | initialization of t | generic.go:21:9:21:9 | s | +| generic.go:21:2:21:12 | return statement | generic.go:22:1:22:1 | exit | +| generic.go:21:9:21:9 | s | generic.go:21:12:21:12 | t | +| generic.go:21:12:21:12 | t | generic.go:21:2:21:12 | return statement | +| generic.go:24:1:24:1 | entry | generic.go:25:2:25:4 | skip | +| generic.go:24:1:35:1 | function declaration | generic.go:0:0:0:0 | exit | +| generic.go:24:6:24:12 | skip | generic.go:24:1:35:1 | function declaration | +| generic.go:25:2:25:4 | assignment to gs1 | generic.go:26:2:26:2 | skip | +| generic.go:25:2:25:4 | skip | generic.go:25:9:25:35 | struct literal | +| generic.go:25:9:25:35 | struct literal | generic.go:25:32:25:34 | "x" | +| generic.go:25:32:25:34 | "x" | generic.go:25:32:25:34 | init of "x" | +| generic.go:25:32:25:34 | init of "x" | generic.go:25:2:25:4 | assignment to gs1 | +| generic.go:26:2:26:2 | assignment to a | generic.go:27:2:27:4 | skip | +| generic.go:26:2:26:2 | skip | generic.go:26:7:26:9 | gs1 | +| generic.go:26:7:26:9 | gs1 | generic.go:26:7:26:18 | selection of Identity | +| generic.go:26:7:26:18 | selection of Identity | generic.go:26:20:26:26 | "hello" | +| generic.go:26:7:26:27 | call to Identity | generic.go:26:2:26:2 | assignment to a | +| generic.go:26:7:26:27 | call to Identity | generic.go:35:1:35:1 | exit | +| generic.go:26:20:26:26 | "hello" | generic.go:26:7:26:27 | call to Identity | +| generic.go:27:2:27:4 | assignment to gs2 | generic.go:28:2:28:2 | skip | +| generic.go:27:2:27:4 | skip | generic.go:27:9:27:48 | struct literal | +| generic.go:27:9:27:48 | struct literal | generic.go:27:40:27:42 | "y" | +| generic.go:27:40:27:42 | "y" | generic.go:27:40:27:42 | init of "y" | +| generic.go:27:40:27:42 | init of "y" | generic.go:27:45:27:47 | "z" | +| generic.go:27:45:27:47 | "z" | generic.go:27:45:27:47 | init of "z" | +| generic.go:27:45:27:47 | init of "z" | generic.go:27:2:27:4 | assignment to gs2 | +| generic.go:28:2:28:2 | assignment to b | generic.go:29:2:29:2 | skip | +| generic.go:28:2:28:2 | skip | generic.go:28:7:28:9 | gs2 | +| generic.go:28:7:28:9 | gs2 | generic.go:28:7:28:19 | selection of Identity1 | +| generic.go:28:7:28:19 | selection of Identity1 | generic.go:28:21:28:21 | a | +| generic.go:28:7:28:22 | call to Identity1 | generic.go:28:2:28:2 | assignment to b | +| generic.go:28:7:28:22 | call to Identity1 | generic.go:35:1:35:1 | exit | +| generic.go:28:21:28:21 | a | generic.go:28:7:28:22 | call to Identity1 | +| generic.go:29:2:29:2 | assignment to c | generic.go:30:2:30:2 | skip | +| generic.go:29:2:29:2 | skip | generic.go:29:7:29:22 | genericIdentity1 | +| generic.go:29:7:29:22 | genericIdentity1 | generic.go:29:32:29:32 | b | +| generic.go:29:7:29:33 | call to genericIdentity1 | generic.go:29:2:29:2 | assignment to c | +| generic.go:29:7:29:33 | call to genericIdentity1 | generic.go:35:1:35:1 | exit | +| generic.go:29:32:29:32 | b | generic.go:29:7:29:33 | call to genericIdentity1 | +| generic.go:30:2:30:2 | assignment to d | generic.go:31:2:31:2 | skip | +| generic.go:30:2:30:2 | skip | generic.go:30:7:30:22 | genericIdentity1 | +| generic.go:30:7:30:22 | genericIdentity1 | generic.go:30:24:30:24 | c | +| generic.go:30:7:30:25 | call to genericIdentity1 | generic.go:30:2:30:2 | assignment to d | +| generic.go:30:7:30:25 | call to genericIdentity1 | generic.go:35:1:35:1 | exit | +| generic.go:30:24:30:24 | c | generic.go:30:7:30:25 | call to genericIdentity1 | +| generic.go:31:2:31:2 | assignment to e | generic.go:31:2:31:53 | ... := ...[1] | +| generic.go:31:2:31:2 | skip | generic.go:31:5:31:5 | skip | +| generic.go:31:2:31:53 | ... := ...[0] | generic.go:31:2:31:2 | assignment to e | +| generic.go:31:2:31:53 | ... := ...[1] | generic.go:31:5:31:5 | assignment to f | +| generic.go:31:5:31:5 | assignment to f | generic.go:32:2:32:2 | skip | +| generic.go:31:5:31:5 | skip | generic.go:31:10:31:25 | genericIdentity2 | +| generic.go:31:10:31:25 | genericIdentity2 | generic.go:31:43:31:43 | d | +| generic.go:31:10:31:53 | call to genericIdentity2 | generic.go:31:2:31:53 | ... := ...[0] | +| generic.go:31:10:31:53 | call to genericIdentity2 | generic.go:35:1:35:1 | exit | +| generic.go:31:43:31:43 | d | generic.go:31:46:31:52 | "hello" | +| generic.go:31:46:31:52 | "hello" | generic.go:31:10:31:53 | call to genericIdentity2 | +| generic.go:32:2:32:2 | assignment to g | generic.go:32:2:32:31 | ... := ...[1] | +| generic.go:32:2:32:2 | skip | generic.go:32:5:32:5 | skip | +| generic.go:32:2:32:31 | ... := ...[0] | generic.go:32:2:32:2 | assignment to g | +| generic.go:32:2:32:31 | ... := ...[1] | generic.go:32:5:32:5 | assignment to h | +| generic.go:32:5:32:5 | assignment to h | generic.go:33:2:33:2 | skip | +| generic.go:32:5:32:5 | skip | generic.go:32:10:32:25 | genericIdentity2 | +| generic.go:32:10:32:25 | genericIdentity2 | generic.go:32:27:32:27 | e | +| generic.go:32:10:32:31 | call to genericIdentity2 | generic.go:32:2:32:31 | ... := ...[0] | +| generic.go:32:10:32:31 | call to genericIdentity2 | generic.go:35:1:35:1 | exit | +| generic.go:32:27:32:27 | e | generic.go:32:30:32:30 | f | +| generic.go:32:30:32:30 | f | generic.go:32:10:32:31 | call to genericIdentity2 | +| generic.go:33:2:33:2 | skip | generic.go:33:6:33:6 | g | +| generic.go:33:6:33:6 | g | generic.go:34:2:34:2 | skip | +| generic.go:34:2:34:2 | skip | generic.go:34:6:34:6 | h | +| generic.go:34:6:34:6 | h | generic.go:35:1:35:1 | exit | | hello.go:0:0:0:0 | entry | hello.go:3:1:3:12 | skip | | hello.go:3:1:3:12 | skip | hello.go:5:7:5:13 | skip | | hello.go:5:7:5:13 | assignment to message | hello.go:7:6:7:13 | skip | diff --git a/ql/test/library-tests/semmle/go/controlflow/ControlFlowGraph/generic.go b/ql/test/library-tests/semmle/go/controlflow/ControlFlowGraph/generic.go new file mode 100644 index 00000000000..264681ab030 --- /dev/null +++ b/ql/test/library-tests/semmle/go/controlflow/ControlFlowGraph/generic.go @@ -0,0 +1,35 @@ +package main + +type GenericStruct1[T any] struct { + Field T +} + +func (g GenericStruct1[U]) Identity(u U) U { return u } + +type GenericStruct2[S, T any] struct { + Field1 S + Field2 T +} + +func (g GenericStruct2[U, _]) Identity1(u U) U { return u } + +func genericIdentity1[T any](t T) T { + return t +} + +func genericIdentity2[S, T any](s S, t T) (S, T) { + return s, t +} + +func generic() { + gs1 := GenericStruct1[string]{"x"} + a := gs1.Identity("hello") + gs2 := GenericStruct2[string, string]{"y", "z"} + b := gs2.Identity1(a) + c := genericIdentity1[string](b) + d := genericIdentity1(c) + e, f := genericIdentity2[string, string](d, "hello") + g, h := genericIdentity2(e, f) + _ = g + _ = h +} From 20562cb43d834138d95d5ac163f629d66fc4fe31 Mon Sep 17 00:00:00 2001 From: Owen Mansel-Chan Date: Tue, 12 Apr 2022 17:06:39 +0100 Subject: [PATCH 30/49] Add missing `this.` to member predicate calls --- ql/lib/semmle/go/AST.qll | 2 +- ql/lib/semmle/go/Types.qll | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/ql/lib/semmle/go/AST.qll b/ql/lib/semmle/go/AST.qll index 6c632af5e9b..a9862ea330f 100644 --- a/ql/lib/semmle/go/AST.qll +++ b/ql/lib/semmle/go/AST.qll @@ -231,7 +231,7 @@ class TypeParamDeclParent extends @typeparamdeclparent, AstNode { /** * Gets a child field of this node in the AST. */ - TypeParamDecl getATypeParameterDecl() { result = getTypeParameterDecl(_) } + TypeParamDecl getATypeParameterDecl() { result = this.getTypeParameterDecl(_) } } /** diff --git a/ql/lib/semmle/go/Types.qll b/ql/lib/semmle/go/Types.qll index e4c664f66ef..c541d75e2a7 100644 --- a/ql/lib/semmle/go/Types.qll +++ b/ql/lib/semmle/go/Types.qll @@ -72,7 +72,7 @@ class Type extends @type { predicate implements(InterfaceType i) { if i = any(ComparableType comparable).getUnderlyingType() then this.implementsComparable() - else implementsNotComparable(i) + else this.implementsNotComparable(i) } /** @@ -691,7 +691,7 @@ class TypeSetLiteralType extends @typesetliteraltype, CompositeType { TypeSetTerm getTerm(int i) { result = MkTypeSetTerm(this, i) } /** Gets a term in this type set literal. */ - TypeSetTerm getATerm() { result = getTerm(_) } + TypeSetTerm getATerm() { result = this.getTerm(_) } /** Holds if `t` is in the type set of this type set literal. */ predicate includesType(Type t) { this.getATerm().includesType(t) } @@ -747,7 +747,7 @@ class InterfaceType extends @interfacetype, CompositeType { * as part of the method set of this interface. */ Type getADirectlyEmbeddedInterface() { - hasDirectlyEmbeddedType(_, result) and result.getUnderlyingType() instanceof InterfaceType + this.hasDirectlyEmbeddedType(_, result) and result.getUnderlyingType() instanceof InterfaceType } /** From a8a351f6ae7509a8c95f5ff977ff8cec290ec336 Mon Sep 17 00:00:00 2001 From: Owen Mansel-Chan Date: Tue, 12 Apr 2022 17:07:05 +0100 Subject: [PATCH 31/49] Improve comment --- ql/lib/semmle/go/Types.qll | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/ql/lib/semmle/go/Types.qll b/ql/lib/semmle/go/Types.qll index c541d75e2a7..08e3c43d0f7 100644 --- a/ql/lib/semmle/go/Types.qll +++ b/ql/lib/semmle/go/Types.qll @@ -697,11 +697,13 @@ class TypeSetLiteralType extends @typesetliteraltype, CompositeType { predicate includesType(Type t) { this.getATerm().includesType(t) } /** - * Gets the interface type specified by just this type set literal, if it - * exists. + * Gets the interface type of which this type-set literal is the only + * element, if it exists. * - * This will exist in cases where the type set literal is used directly as - * the bound in a type parameter declaration. + * It exists if it has been explicitly defined, as in + * `interface { int64 | uint64 }`, or if it has been implicitly created by + * using the type set literal directly as the bound in a type parameter + * declaration, as in `[T int64 | uint64]`. */ InterfaceType getInterfaceType() { this = result.getDirectlyEmbeddedTypeSetLiteral(0) and From 0dee215e8c8eb663b3dca597eeafe0fba0cbc961 Mon Sep 17 00:00:00 2001 From: Owen Mansel-Chan Date: Wed, 13 Apr 2022 11:16:48 +0100 Subject: [PATCH 32/49] Update CodeQL tests to go 1.18.1 --- .github/workflows/codeqltest.yml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/codeqltest.yml b/.github/workflows/codeqltest.yml index e07d5f443b3..54e2e4d0a71 100644 --- a/.github/workflows/codeqltest.yml +++ b/.github/workflows/codeqltest.yml @@ -7,10 +7,10 @@ jobs: runs-on: ubuntu-latest steps: - - name: Set up Go 1.18 + - name: Set up Go 1.18.1 uses: actions/setup-go@v1 with: - go-version: 1.18 + go-version: 1.18.1 id: go - name: Set up CodeQL CLI @@ -59,10 +59,10 @@ jobs: name: Test MacOS runs-on: macOS-latest steps: - - name: Set up Go 1.18 + - name: Set up Go 1.18.1 uses: actions/setup-go@v1 with: - go-version: 1.18 + go-version: 1.18.1 id: go - name: Set up CodeQL CLI @@ -99,10 +99,10 @@ jobs: name: Test Windows runs-on: windows-2019 steps: - - name: Set up Go 1.18 + - name: Set up Go 1.18.1 uses: actions/setup-go@v1 with: - go-version: 1.18 + go-version: 1.18.1 id: go - name: Set up CodeQL CLI From 3790c4eb4d6644aeef01886ec3481283f9bc89b0 Mon Sep 17 00:00:00 2001 From: Owen Mansel-Chan Date: Wed, 13 Apr 2022 16:13:51 +0100 Subject: [PATCH 33/49] Control flow for generic function instantiations --- ql/lib/semmle/go/controlflow/ControlFlowGraphImpl.qll | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/ql/lib/semmle/go/controlflow/ControlFlowGraphImpl.qll b/ql/lib/semmle/go/controlflow/ControlFlowGraphImpl.qll index 743b8e4fae6..25137f75a96 100644 --- a/ql/lib/semmle/go/controlflow/ControlFlowGraphImpl.qll +++ b/ql/lib/semmle/go/controlflow/ControlFlowGraphImpl.qll @@ -698,6 +698,10 @@ module CFG { not this.(SelectorExpr).getBase() instanceof ValueExpr and nd = mkExprOrSkipNode(this) and cmpl = Done() + or + this instanceof GenericFunctionInstantiationExpr and + nd = MkExprNode(this) and + cmpl = Done() } override predicate firstNode(ControlFlow::Node first) { first = nd } From 4a9aeacb69d8eb3d7e9b89689ad4e910baf628ba Mon Sep 17 00:00:00 2001 From: Owen Mansel-Chan Date: Wed, 13 Apr 2022 16:15:00 +0100 Subject: [PATCH 34/49] Find callee through function instantiation --- ql/lib/semmle/go/dataflow/internal/DataFlowNodes.qll | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ql/lib/semmle/go/dataflow/internal/DataFlowNodes.qll b/ql/lib/semmle/go/dataflow/internal/DataFlowNodes.qll index 91e97270cf6..80c94cafdaa 100644 --- a/ql/lib/semmle/go/dataflow/internal/DataFlowNodes.qll +++ b/ql/lib/semmle/go/dataflow/internal/DataFlowNodes.qll @@ -443,7 +443,8 @@ module Public { */ private DataFlow::Node getACalleeSource(DataFlow::CallNode cn) { result = cn.getCalleeNode() or - basicLocalFlowStep(result, getACalleeSource(cn)) + basicLocalFlowStep(result, getACalleeSource(cn)) or + result.asExpr() = getACalleeSource(cn).asExpr().(GenericFunctionInstantiationExpr).getBase() } /** A data flow node that represents a call. */ From ba147e8661bb04d1ef68b49f14eb270708252f56 Mon Sep 17 00:00:00 2001 From: Owen Mansel-Chan Date: Thu, 21 Apr 2022 22:33:30 +0100 Subject: [PATCH 35/49] Test calls through variables The tests which involve a flow through a receiver with a non-trivial access path currently don't give the right result. This should be fixed in a follow-up issue. --- .../genericfunctions.go | 19 +++++++ .../generictypesandmethods.go | 51 +++++++++++++++++++ 2 files changed, 70 insertions(+) diff --git a/ql/test/library-tests/semmle/go/dataflow/GenericFunctionsAndTypes/genericfunctions.go b/ql/test/library-tests/semmle/go/dataflow/GenericFunctionsAndTypes/genericfunctions.go index 2ab18e235f8..b82420511e6 100644 --- a/ql/test/library-tests/semmle/go/dataflow/GenericFunctionsAndTypes/genericfunctions.go +++ b/ql/test/library-tests/semmle/go/dataflow/GenericFunctionsAndTypes/genericfunctions.go @@ -17,6 +17,10 @@ func genericSink2[T any](t T, u string) { sink(u) // $ hasValueFlow="u" } +func genericSink3[T any](t T, u string) { + sink(u) // $ hasValueFlow="u" +} + func test() { var x struct { x1 int @@ -30,6 +34,11 @@ func test() { s := genericSource[int8](2) sink(s) // $ hasValueFlow="s" } + { + f := genericSource[int8] + s := f(2) + sink(s) // $ hasValueFlow="s" + } { s := genericIdentity(source()) sink(s) // $ hasValueFlow="s" @@ -38,6 +47,11 @@ func test() { s := genericIdentity[string](source()) sink(s) // $ hasValueFlow="s" } + { + f := genericIdentity[string] + s := f(source()) + sink(s) // $ hasValueFlow="s" + } { s := source() genericSink1(x, s) @@ -46,4 +60,9 @@ func test() { s := source() genericSink2[uint16](3, s) } + { + s := source() + f := genericSink3[uint16] + f(3, s) + } } diff --git a/ql/test/library-tests/semmle/go/dataflow/GenericFunctionsAndTypes/generictypesandmethods.go b/ql/test/library-tests/semmle/go/dataflow/GenericFunctionsAndTypes/generictypesandmethods.go index 18d42d0e934..4e2dc169c6d 100644 --- a/ql/test/library-tests/semmle/go/dataflow/GenericFunctionsAndTypes/generictypesandmethods.go +++ b/ql/test/library-tests/semmle/go/dataflow/GenericFunctionsAndTypes/generictypesandmethods.go @@ -37,16 +37,33 @@ func main() { gs1 := GenericStruct1[string]{""} sink(gs1.Identity(source())) // $ hasValueFlow="call to Identity" } + { + gs1 := GenericStruct1[string]{""} + f := gs1.Identity + sink(f(source())) // $ hasValueFlow="call to f" + } { gs1 := GenericStruct1[string]{""} gs1.Field = source() sink(gs1.Getter()) // $ hasValueFlow="call to Getter" } + { + gs1 := GenericStruct1[string]{""} + gs1.Field = source() + f := gs1.Getter + sink(f()) // $ MISSING: hasValueFlow="call to f" + } { gs1 := GenericStruct1[string]{""} gs1.Setter(source()) sink(gs1.Field) // $ hasValueFlow="selection of Field" } + { + gs1 := GenericStruct1[string]{""} + f := gs1.Setter + f(source()) + sink(gs1.Field) // $ MISSING: hasValueFlow="selection of Field" + } { gs2 := GenericStruct2[string, string]{source(), ""} @@ -56,16 +73,33 @@ func main() { gs2 := GenericStruct2[string, string]{"", ""} sink(gs2.Identity1(source())) // $ hasValueFlow="call to Identity1" } + { + gs2 := GenericStruct2[string, string]{"", ""} + f := gs2.Identity1 + sink(f(source())) // $ hasValueFlow="call to f" + } { gs2 := GenericStruct2[string, string]{"", ""} gs2.Field1 = source() sink(gs2.Getter1()) // $ hasValueFlow="call to Getter1" } + { + gs2 := GenericStruct2[string, string]{"", ""} + gs2.Field1 = source() + f := gs2.Getter1 + sink(f()) // $ MISSING: hasValueFlow="call to f" + } { gs2 := GenericStruct2[string, string]{"", ""} gs2.Setter1(source()) sink(gs2.Field1) // $ hasValueFlow="selection of Field1" } + { + gs2 := GenericStruct2[string, string]{"", ""} + f := gs2.Setter1 + f(source()) + sink(gs2.Field1) // $ MISSING: hasValueFlow="selection of Field1" + } { gs2 := GenericStruct2[string, string]{"", source()} @@ -75,14 +109,31 @@ func main() { gs2 := GenericStruct2[string, string]{"", ""} sink(gs2.Identity2(source())) // $ hasValueFlow="call to Identity2" } + { + gs2 := GenericStruct2[string, string]{"", ""} + f := gs2.Identity2 + sink(f(source())) // $ hasValueFlow="call to f" + } { gs2 := GenericStruct2[string, string]{"", ""} gs2.Field2 = source() sink(gs2.Getter2()) // $ hasValueFlow="call to Getter2" } + { + gs2 := GenericStruct2[string, string]{"", ""} + gs2.Field2 = source() + f := gs2.Getter2 + sink(f()) // $ MISSING: hasValueFlow="call to f" + } { gs2 := GenericStruct2[string, string]{"", ""} gs2.Setter2(source()) sink(gs2.Field2) // $ hasValueFlow="selection of Field2" } + { + gs2 := GenericStruct2[string, string]{"", ""} + f := gs2.Setter2 + f(source()) + sink(gs2.Field2) // $ MISSING: hasValueFlow="selection of Field2" + } } From 2e8b9a9a7d645df90ef6ddeb371c09e217193a1f Mon Sep 17 00:00:00 2001 From: Owen Mansel-Chan Date: Mon, 25 Apr 2022 14:44:35 +0100 Subject: [PATCH 36/49] Fix extractor crash when missing type information --- extractor/extractor.go | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/extractor/extractor.go b/extractor/extractor.go index aafea417185..3bbda59fc8f 100644 --- a/extractor/extractor.go +++ b/extractor/extractor.go @@ -918,13 +918,21 @@ func extractExpr(tw *trap.Writer, expr ast.Expr, parent trap.Label, idx int) { if expr == nil { return } - if _, ok := typeOf(tw, expr.X).Underlying().(*types.Signature); ok { - kind = dbscheme.GenericFunctionInstantiationExpr.Index() - } else { - // Can't distinguish between actual index expressions (into a map, - // array, slice, string or pointer to array) and generic type - // specialization expression, so we do it later in QL. + typeofx := typeOf(tw, expr.X) + if typeofx == nil { + // We are missing type information for `expr.X`, so we cannot + // determine whether this is a generic function instantiation + // or not. kind = dbscheme.IndexExpr.Index() + } else { + if _, ok := typeofx.Underlying().(*types.Signature); ok { + kind = dbscheme.GenericFunctionInstantiationExpr.Index() + } else { + // Can't distinguish between actual index expressions (into a + // map, array, slice, string or pointer to array) and generic + // type specialization expression, so we do it later in QL. + kind = dbscheme.IndexExpr.Index() + } } extractExpr(tw, expr.X, lbl, 0) extractExpr(tw, expr.Index, lbl, 1) From 06d139848d5fc2710f93889a49e0c5f0073429d1 Mon Sep 17 00:00:00 2001 From: Owen Mansel-Chan Date: Tue, 26 Apr 2022 11:59:15 +0100 Subject: [PATCH 37/49] Fix panic when type is unknown --- extractor/extractor.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/extractor/extractor.go b/extractor/extractor.go index 3bbda59fc8f..f3826e3ad7f 100644 --- a/extractor/extractor.go +++ b/extractor/extractor.go @@ -1815,6 +1815,9 @@ func extractNumLines(tw *trap.Writer, fileName string, ast *ast.File) { // of an interface must be a method signature, an embedded interface type or a // type set literal. func isNonUnionTypeSetLiteral(t types.Type) bool { + if t == nil { + return false + } switch t.Underlying().(type) { case *types.Interface, *types.Union, *types.Signature: return false From aa62fabe26a4e1ee1674010f65da00f2d88920b3 Mon Sep 17 00:00:00 2001 From: Owen Mansel-Chan Date: Tue, 26 Apr 2022 13:50:27 +0100 Subject: [PATCH 38/49] Fix another place where type could be nil --- extractor/extractor.go | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/extractor/extractor.go b/extractor/extractor.go index f3826e3ad7f..7f7f1cae677 100644 --- a/extractor/extractor.go +++ b/extractor/extractor.go @@ -940,10 +940,18 @@ func extractExpr(tw *trap.Writer, expr ast.Expr, parent trap.Label, idx int) { if expr == nil { return } - if _, ok := typeOf(tw, expr.X).Underlying().(*types.Signature); ok { - kind = dbscheme.GenericFunctionInstantiationExpr.Index() - } else { + typeofx := typeOf(tw, expr.X) + if typeofx == nil { + // We are missing type information for `expr.X`, so we cannot + // determine whether this is a generic function instantiation + // or not. kind = dbscheme.GenericTypeInstantiationExpr.Index() + } else { + if _, ok := typeofx.Underlying().(*types.Signature); ok { + kind = dbscheme.GenericFunctionInstantiationExpr.Index() + } else { + kind = dbscheme.GenericTypeInstantiationExpr.Index() + } } extractExpr(tw, expr.X, lbl, 0) extractExprs(tw, expr.Indices, lbl, 1, 1) From 7f1f428b41149932f01f96dbb2b840d53058b053 Mon Sep 17 00:00:00 2001 From: Owen Mansel-Chan Date: Wed, 27 Apr 2022 15:47:53 +0100 Subject: [PATCH 39/49] Remove invalid code in test "type declarations inside generic functions are not currently supported" --- .../semmle/go/Function/TypeParamType.expected | 3 --- .../semmle/go/Function/genericFunctions.go | 13 ------------- .../semmle/go/Function/getParameter.expected | 9 ++++----- .../semmle/go/Function/getTypeParameter.expected | 15 ++++++--------- 4 files changed, 10 insertions(+), 30 deletions(-) diff --git a/ql/test/library-tests/semmle/go/Function/TypeParamType.expected b/ql/test/library-tests/semmle/go/Function/TypeParamType.expected index 6c1437fd468..661cc77556f 100644 --- a/ql/test/library-tests/semmle/go/Function/TypeParamType.expected +++ b/ql/test/library-tests/semmle/go/Function/TypeParamType.expected @@ -1,6 +1,3 @@ -| A | comparable | -| B1 | interface { } | -| B2 | interface { } | | Edge | EdgeConstraint | | Edge | interface { } | | K | comparable | diff --git a/ql/test/library-tests/semmle/go/Function/genericFunctions.go b/ql/test/library-tests/semmle/go/Function/genericFunctions.go index f43fa1d02d9..efa254233a2 100644 --- a/ql/test/library-tests/semmle/go/Function/genericFunctions.go +++ b/ql/test/library-tests/semmle/go/Function/genericFunctions.go @@ -119,19 +119,6 @@ func (l *List[U]) MyLen() int { return 0 } -func outerFunction[A comparable](x1 A) { - type innerStruct[B1 any] struct { - x1 A - x2 B1 - } - type innerMap[B2 any] map[A]B2 - - var x innerStruct[string] - _ = x - var y innerMap[bool] - _ = y -} - type NodeConstraint[Edge any] interface { Edges() []Edge } diff --git a/ql/test/library-tests/semmle/go/Function/getParameter.expected b/ql/test/library-tests/semmle/go/Function/getParameter.expected index 7b7ab23e09d..84d34db4c17 100644 --- a/ql/test/library-tests/semmle/go/Function/getParameter.expected +++ b/ql/test/library-tests/semmle/go/Function/getParameter.expected @@ -5,11 +5,10 @@ | genericFunctions.go:93:35:93:36 | f2 | -1 | genericFunctions.go:93:7:93:7 | x | | genericFunctions.go:96:36:96:37 | g2 | -1 | genericFunctions.go:96:7:96:7 | x | | genericFunctions.go:118:19:118:23 | MyLen | -1 | genericFunctions.go:118:7:118:7 | l | -| genericFunctions.go:122:6:122:18 | outerFunction | 0 | genericFunctions.go:122:34:122:35 | x1 | -| genericFunctions.go:145:6:145:8 | New | 0 | genericFunctions.go:145:64:145:68 | nodes | -| genericFunctions.go:149:29:149:40 | ShortestPath | 0 | genericFunctions.go:149:42:149:45 | from | -| genericFunctions.go:149:29:149:40 | ShortestPath | 1 | genericFunctions.go:149:48:149:49 | to | -| genericFunctions.go:149:29:149:40 | ShortestPath | -1 | genericFunctions.go:149:7:149:7 | g | +| genericFunctions.go:132:6:132:8 | New | 0 | genericFunctions.go:132:64:132:68 | nodes | +| genericFunctions.go:136:29:136:40 | ShortestPath | 0 | genericFunctions.go:136:42:136:45 | from | +| genericFunctions.go:136:29:136:40 | ShortestPath | 1 | genericFunctions.go:136:48:136:49 | to | +| genericFunctions.go:136:29:136:40 | ShortestPath | -1 | genericFunctions.go:136:7:136:7 | g | | 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 | diff --git a/ql/test/library-tests/semmle/go/Function/getTypeParameter.expected b/ql/test/library-tests/semmle/go/Function/getTypeParameter.expected index 383938aaff5..9756d668b19 100644 --- a/ql/test/library-tests/semmle/go/Function/getTypeParameter.expected +++ b/ql/test/library-tests/semmle/go/Function/getTypeParameter.expected @@ -6,12 +6,9 @@ | genericFunctions.go:82:6:83:1 | type declaration specifier | TypeSpec | 0 | genericFunctions.go:82:21:82:28 | type parameter declaration | 1 | genericFunctions.go:82:24:82:24 | T | genericFunctions.go:82:26:82:28 | any | interface { } | | genericFunctions.go:109:6:111:1 | type declaration specifier | TypeSpec | 0 | genericFunctions.go:109:14:109:18 | type parameter declaration | 0 | genericFunctions.go:109:14:109:14 | S | genericFunctions.go:109:16:109:18 | any | interface { } | | genericFunctions.go:113:6:115:1 | type declaration specifier | TypeSpec | 0 | genericFunctions.go:113:11:113:15 | type parameter declaration | 0 | genericFunctions.go:113:11:113:11 | T | genericFunctions.go:113:13:113:15 | any | interface { } | -| genericFunctions.go:122:1:133:1 | function declaration | FuncDecl | 0 | genericFunctions.go:122:20:122:31 | type parameter declaration | 0 | genericFunctions.go:122:20:122:20 | A | genericFunctions.go:122:22:122:31 | comparable | comparable | -| genericFunctions.go:123:7:126:2 | type declaration specifier | TypeSpec | 0 | genericFunctions.go:123:19:123:24 | type parameter declaration | 0 | genericFunctions.go:123:19:123:20 | B1 | genericFunctions.go:123:22:123:24 | any | interface { } | -| genericFunctions.go:127:7:127:31 | type declaration specifier | TypeSpec | 0 | genericFunctions.go:127:16:127:21 | type parameter declaration | 0 | genericFunctions.go:127:16:127:17 | B2 | genericFunctions.go:127:19:127:21 | any | interface { } | -| genericFunctions.go:135:6:137:1 | type declaration specifier | TypeSpec | 0 | genericFunctions.go:135:21:135:28 | type parameter declaration | 0 | genericFunctions.go:135:21:135:24 | Edge | genericFunctions.go:135:26:135:28 | any | interface { } | -| genericFunctions.go:139:6:141:1 | type declaration specifier | TypeSpec | 0 | genericFunctions.go:139:21:139:28 | type parameter declaration | 0 | genericFunctions.go:139:21:139:24 | Node | genericFunctions.go:139:26:139:28 | any | interface { } | -| genericFunctions.go:143:6:143:73 | type declaration specifier | TypeSpec | 0 | genericFunctions.go:143:12:143:36 | type parameter declaration | 0 | genericFunctions.go:143:12:143:15 | Node | genericFunctions.go:143:17:143:36 | generic type instantiation expression | NodeConstraint | -| genericFunctions.go:143:6:143:73 | type declaration specifier | TypeSpec | 1 | genericFunctions.go:143:39:143:63 | type parameter declaration | 0 | genericFunctions.go:143:39:143:42 | Edge | genericFunctions.go:143:44:143:63 | generic type instantiation expression | EdgeConstraint | -| genericFunctions.go:145:1:147:1 | function declaration | FuncDecl | 0 | genericFunctions.go:145:10:145:34 | type parameter declaration | 0 | genericFunctions.go:145:10:145:13 | Node | genericFunctions.go:145:15:145:34 | generic type instantiation expression | NodeConstraint | -| genericFunctions.go:145:1:147:1 | function declaration | FuncDecl | 1 | genericFunctions.go:145:37:145:61 | type parameter declaration | 0 | genericFunctions.go:145:37:145:40 | Edge | genericFunctions.go:145:42:145:61 | generic type instantiation expression | EdgeConstraint | +| genericFunctions.go:122:6:124:1 | type declaration specifier | TypeSpec | 0 | genericFunctions.go:122:21:122:28 | type parameter declaration | 0 | genericFunctions.go:122:21:122:24 | Edge | genericFunctions.go:122:26:122:28 | any | interface { } | +| genericFunctions.go:126:6:128:1 | type declaration specifier | TypeSpec | 0 | genericFunctions.go:126:21:126:28 | type parameter declaration | 0 | genericFunctions.go:126:21:126:24 | Node | genericFunctions.go:126:26:126:28 | any | interface { } | +| genericFunctions.go:130:6:130:73 | type declaration specifier | TypeSpec | 0 | genericFunctions.go:130:12:130:36 | type parameter declaration | 0 | genericFunctions.go:130:12:130:15 | Node | genericFunctions.go:130:17:130:36 | generic type instantiation expression | NodeConstraint | +| genericFunctions.go:130:6:130:73 | type declaration specifier | TypeSpec | 1 | genericFunctions.go:130:39:130:63 | type parameter declaration | 0 | genericFunctions.go:130:39:130:42 | Edge | genericFunctions.go:130:44:130:63 | generic type instantiation expression | EdgeConstraint | +| genericFunctions.go:132:1:134:1 | function declaration | FuncDecl | 0 | genericFunctions.go:132:10:132:34 | type parameter declaration | 0 | genericFunctions.go:132:10:132:13 | Node | genericFunctions.go:132:15:132:34 | generic type instantiation expression | NodeConstraint | +| genericFunctions.go:132:1:134:1 | function declaration | FuncDecl | 1 | genericFunctions.go:132:37:132:61 | type parameter declaration | 0 | genericFunctions.go:132:37:132:40 | Edge | genericFunctions.go:132:42:132:61 | generic type instantiation expression | EdgeConstraint | From 8477053c90948de8443383edd5c4f3141eddffb7 Mon Sep 17 00:00:00 2001 From: Owen Mansel-Chan Date: Wed, 27 Apr 2022 15:56:17 +0100 Subject: [PATCH 40/49] Test calling generic functions from other files --- .../GenericFunctionInstantiationExpr.expected | 14 +++++---- .../semmle/go/Function/genericFunctions.go | 12 ++++++++ .../semmle/go/Function/genericFunctions2.go | 6 ++++ .../semmle/go/Function/getParameter.expected | 23 ++++++++------- .../go/Function/getTypeParameter.expected | 29 ++++++++++--------- .../library-tests/semmle/go/Function/go.mod | 6 ++++ .../vendor/github.com/anotherpkg/README.md | 1 + .../github.com/anotherpkg/anotherpkg.go | 6 ++++ .../semmle/go/Function/vendor/modules.txt | 3 ++ 9 files changed, 69 insertions(+), 31 deletions(-) create mode 100644 ql/test/library-tests/semmle/go/Function/genericFunctions2.go create mode 100644 ql/test/library-tests/semmle/go/Function/go.mod create mode 100644 ql/test/library-tests/semmle/go/Function/vendor/github.com/anotherpkg/README.md create mode 100644 ql/test/library-tests/semmle/go/Function/vendor/github.com/anotherpkg/anotherpkg.go create mode 100644 ql/test/library-tests/semmle/go/Function/vendor/modules.txt diff --git a/ql/test/library-tests/semmle/go/Function/GenericFunctionInstantiationExpr.expected b/ql/test/library-tests/semmle/go/Function/GenericFunctionInstantiationExpr.expected index 8ee6c980c10..6528a0ae7f3 100644 --- a/ql/test/library-tests/semmle/go/Function/GenericFunctionInstantiationExpr.expected +++ b/ql/test/library-tests/semmle/go/Function/GenericFunctionInstantiationExpr.expected @@ -1,6 +1,8 @@ -| genericFunctions.go:23:2:23:33 | generic function instantiation expression | genericFunctions.go:23:2:23:28 | GenericFunctionOneTypeParam | 0 | genericFunctions.go:23:30:23:32 | int | -| genericFunctions.go:24:2:24:36 | generic function instantiation expression | genericFunctions.go:24:2:24:28 | GenericFunctionOneTypeParam | 0 | genericFunctions.go:24:30:24:35 | string | -| genericFunctions.go:42:6:42:48 | generic function instantiation expression | genericFunctions.go:42:6:42:33 | GenericFunctionTwoTypeParams | 0 | genericFunctions.go:42:35:42:40 | string | -| genericFunctions.go:42:6:42:48 | generic function instantiation expression | genericFunctions.go:42:6:42:33 | GenericFunctionTwoTypeParams | 1 | genericFunctions.go:42:43:42:47 | int64 | -| genericFunctions.go:43:6:43:50 | generic function instantiation expression | genericFunctions.go:43:6:43:33 | GenericFunctionTwoTypeParams | 0 | genericFunctions.go:43:35:43:40 | string | -| genericFunctions.go:43:6:43:50 | generic function instantiation expression | genericFunctions.go:43:6:43:33 | GenericFunctionTwoTypeParams | 1 | genericFunctions.go:43:43:43:49 | float64 | +| genericFunctions.go:25:2:25:33 | generic function instantiation expression | genericFunctions.go:25:2:25:28 | GenericFunctionOneTypeParam | 0 | genericFunctions.go:25:30:25:32 | int | +| genericFunctions.go:26:2:26:36 | generic function instantiation expression | genericFunctions.go:26:2:26:28 | GenericFunctionOneTypeParam | 0 | genericFunctions.go:26:30:26:35 | string | +| genericFunctions.go:44:6:44:48 | generic function instantiation expression | genericFunctions.go:44:6:44:33 | GenericFunctionTwoTypeParams | 0 | genericFunctions.go:44:35:44:40 | string | +| genericFunctions.go:44:6:44:48 | generic function instantiation expression | genericFunctions.go:44:6:44:33 | GenericFunctionTwoTypeParams | 1 | genericFunctions.go:44:43:44:47 | int64 | +| genericFunctions.go:45:6:45:50 | generic function instantiation expression | genericFunctions.go:45:6:45:33 | GenericFunctionTwoTypeParams | 0 | genericFunctions.go:45:35:45:40 | string | +| 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 | diff --git a/ql/test/library-tests/semmle/go/Function/genericFunctions.go b/ql/test/library-tests/semmle/go/Function/genericFunctions.go index efa254233a2..43d0a11dd9f 100644 --- a/ql/test/library-tests/semmle/go/Function/genericFunctions.go +++ b/ql/test/library-tests/semmle/go/Function/genericFunctions.go @@ -1,5 +1,7 @@ package main +import "github.com/anotherpkg" + type T1 map[string][]string type T2 T1 @@ -134,3 +136,13 @@ func New[Node NodeConstraint[Edge], Edge EdgeConstraint[Node]](nodes []Node) *Gr } func (g *Graph[Node, Edge]) ShortestPath(from, to Node) []Edge { return []Edge{} } + +func callFunctionsInAnotherFile() { + _ = GenericFunctionInAnotherFile[string]("world") + _ = GenericFunctionInAnotherFile("world") +} + +func callFunctionsInAnotherPackage() { + _ = anotherpkg.GenericFunctionInAnotherPackage[string]("world") + _ = anotherpkg.GenericFunctionInAnotherPackage("world") +} diff --git a/ql/test/library-tests/semmle/go/Function/genericFunctions2.go b/ql/test/library-tests/semmle/go/Function/genericFunctions2.go new file mode 100644 index 00000000000..9394357dea0 --- /dev/null +++ b/ql/test/library-tests/semmle/go/Function/genericFunctions2.go @@ -0,0 +1,6 @@ +package main + +func GenericFunctionInAnotherFile[T any](t T) T { + var r T + return r +} diff --git a/ql/test/library-tests/semmle/go/Function/getParameter.expected b/ql/test/library-tests/semmle/go/Function/getParameter.expected index 84d34db4c17..538b6ff3567 100644 --- a/ql/test/library-tests/semmle/go/Function/getParameter.expected +++ b/ql/test/library-tests/semmle/go/Function/getParameter.expected @@ -1,14 +1,15 @@ -| genericFunctions.go:7:6:7:32 | GenericFunctionOneTypeParam | 0 | genericFunctions.go:7:41:7:41 | t | -| genericFunctions.go:13:6:13:33 | GenericFunctionTwoTypeParams | 0 | genericFunctions.go:13:68:13:68 | m | -| genericFunctions.go:85:30:85:31 | f1 | -1 | genericFunctions.go:85:7:85:7 | x | -| genericFunctions.go:90:31:90:32 | g1 | -1 | genericFunctions.go:90:7:90:7 | x | -| genericFunctions.go:93:35:93:36 | f2 | -1 | genericFunctions.go:93:7:93:7 | x | -| genericFunctions.go:96:36:96:37 | g2 | -1 | genericFunctions.go:96:7:96:7 | x | -| genericFunctions.go:118:19:118:23 | MyLen | -1 | genericFunctions.go:118:7:118:7 | l | -| genericFunctions.go:132:6:132:8 | New | 0 | genericFunctions.go:132:64:132:68 | nodes | -| genericFunctions.go:136:29:136:40 | ShortestPath | 0 | genericFunctions.go:136:42:136:45 | from | -| genericFunctions.go:136:29:136:40 | ShortestPath | 1 | genericFunctions.go:136:48:136:49 | to | -| genericFunctions.go:136:29:136:40 | ShortestPath | -1 | genericFunctions.go:136:7:136:7 | g | +| genericFunctions2.go:3:6:3:33 | GenericFunctionInAnotherFile | 0 | genericFunctions2.go:3:42:3:42 | t | +| genericFunctions.go:9:6:9:32 | GenericFunctionOneTypeParam | 0 | genericFunctions.go:9:41:9:41 | t | +| genericFunctions.go:15:6:15:33 | GenericFunctionTwoTypeParams | 0 | genericFunctions.go:15:68:15:68 | m | +| genericFunctions.go:87:30:87:31 | f1 | -1 | genericFunctions.go:87:7:87:7 | x | +| genericFunctions.go:92:31:92:32 | g1 | -1 | genericFunctions.go:92:7:92:7 | x | +| genericFunctions.go:95:35:95:36 | f2 | -1 | genericFunctions.go:95:7:95:7 | x | +| genericFunctions.go:98:36:98:37 | g2 | -1 | genericFunctions.go:98:7:98:7 | x | +| genericFunctions.go:120:19:120:23 | MyLen | -1 | genericFunctions.go:120:7:120:7 | l | +| genericFunctions.go:134:6:134:8 | New | 0 | genericFunctions.go:134:64:134:68 | nodes | +| genericFunctions.go:138:29:138:40 | ShortestPath | 0 | genericFunctions.go:138:42:138:45 | from | +| 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 | | 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 | diff --git a/ql/test/library-tests/semmle/go/Function/getTypeParameter.expected b/ql/test/library-tests/semmle/go/Function/getTypeParameter.expected index 9756d668b19..ce72080bd6f 100644 --- a/ql/test/library-tests/semmle/go/Function/getTypeParameter.expected +++ b/ql/test/library-tests/semmle/go/Function/getTypeParameter.expected @@ -1,14 +1,15 @@ -| genericFunctions.go:7:1:10:1 | function declaration | FuncDecl | 0 | genericFunctions.go:7:34:7:38 | type parameter declaration | 0 | genericFunctions.go:7:34:7:34 | T | genericFunctions.go:7:36:7:38 | any | interface { } | -| genericFunctions.go:13:1:19:1 | function declaration | FuncDecl | 0 | genericFunctions.go:13:35:13:46 | type parameter declaration | 0 | genericFunctions.go:13:35:13:35 | K | genericFunctions.go:13:37:13:46 | comparable | comparable | -| genericFunctions.go:13:1:19:1 | function declaration | FuncDecl | 1 | genericFunctions.go:13:49:13:65 | type parameter declaration | 0 | genericFunctions.go:13:49:13:49 | V | genericFunctions.go:13:51:13:65 | type set literal | interface { int64 \| float64 } | -| genericFunctions.go:79:6:80:1 | type declaration specifier | TypeSpec | 0 | genericFunctions.go:79:21:79:25 | type parameter declaration | 0 | genericFunctions.go:79:21:79:21 | T | genericFunctions.go:79:23:79:25 | any | interface { } | -| genericFunctions.go:82:6:83:1 | type declaration specifier | TypeSpec | 0 | genericFunctions.go:82:21:82:28 | type parameter declaration | 0 | genericFunctions.go:82:21:82:21 | S | genericFunctions.go:82:26:82:28 | any | interface { } | -| genericFunctions.go:82:6:83:1 | type declaration specifier | TypeSpec | 0 | genericFunctions.go:82:21:82:28 | type parameter declaration | 1 | genericFunctions.go:82:24:82:24 | T | genericFunctions.go:82:26:82:28 | any | interface { } | -| genericFunctions.go:109:6:111:1 | type declaration specifier | TypeSpec | 0 | genericFunctions.go:109:14:109:18 | type parameter declaration | 0 | genericFunctions.go:109:14:109:14 | S | genericFunctions.go:109:16:109:18 | any | interface { } | -| genericFunctions.go:113:6:115:1 | type declaration specifier | TypeSpec | 0 | genericFunctions.go:113:11:113:15 | type parameter declaration | 0 | genericFunctions.go:113:11:113:11 | T | genericFunctions.go:113:13:113:15 | any | interface { } | -| genericFunctions.go:122:6:124:1 | type declaration specifier | TypeSpec | 0 | genericFunctions.go:122:21:122:28 | type parameter declaration | 0 | genericFunctions.go:122:21:122:24 | Edge | genericFunctions.go:122:26:122:28 | any | interface { } | -| genericFunctions.go:126:6:128:1 | type declaration specifier | TypeSpec | 0 | genericFunctions.go:126:21:126:28 | type parameter declaration | 0 | genericFunctions.go:126:21:126:24 | Node | genericFunctions.go:126:26:126:28 | any | interface { } | -| genericFunctions.go:130:6:130:73 | type declaration specifier | TypeSpec | 0 | genericFunctions.go:130:12:130:36 | type parameter declaration | 0 | genericFunctions.go:130:12:130:15 | Node | genericFunctions.go:130:17:130:36 | generic type instantiation expression | NodeConstraint | -| genericFunctions.go:130:6:130:73 | type declaration specifier | TypeSpec | 1 | genericFunctions.go:130:39:130:63 | type parameter declaration | 0 | genericFunctions.go:130:39:130:42 | Edge | genericFunctions.go:130:44:130:63 | generic type instantiation expression | EdgeConstraint | -| genericFunctions.go:132:1:134:1 | function declaration | FuncDecl | 0 | genericFunctions.go:132:10:132:34 | type parameter declaration | 0 | genericFunctions.go:132:10:132:13 | Node | genericFunctions.go:132:15:132:34 | generic type instantiation expression | NodeConstraint | -| genericFunctions.go:132:1:134:1 | function declaration | FuncDecl | 1 | genericFunctions.go:132:37:132:61 | type parameter declaration | 0 | genericFunctions.go:132:37:132:40 | Edge | genericFunctions.go:132:42:132:61 | generic type instantiation expression | EdgeConstraint | +| genericFunctions2.go:3:1:6:1 | function declaration | FuncDecl | 0 | genericFunctions2.go:3:35:3:39 | type parameter declaration | 0 | genericFunctions2.go:3:35:3:35 | T | genericFunctions2.go:3:37:3:39 | any | interface { } | +| genericFunctions.go:9:1:12:1 | function declaration | FuncDecl | 0 | genericFunctions.go:9:34:9:38 | type parameter declaration | 0 | genericFunctions.go:9:34:9:34 | T | genericFunctions.go:9:36:9:38 | any | interface { } | +| genericFunctions.go:15:1:21:1 | function declaration | FuncDecl | 0 | genericFunctions.go:15:35:15:46 | type parameter declaration | 0 | genericFunctions.go:15:35:15:35 | K | genericFunctions.go:15:37:15:46 | comparable | comparable | +| genericFunctions.go:15:1:21:1 | function declaration | FuncDecl | 1 | genericFunctions.go:15:49:15:65 | type parameter declaration | 0 | genericFunctions.go:15:49:15:49 | V | genericFunctions.go:15:51:15:65 | type set literal | interface { int64 \| float64 } | +| genericFunctions.go:81:6:82:1 | type declaration specifier | TypeSpec | 0 | genericFunctions.go:81:21:81:25 | type parameter declaration | 0 | genericFunctions.go:81:21:81:21 | T | genericFunctions.go:81:23:81:25 | any | interface { } | +| genericFunctions.go:84:6:85:1 | type declaration specifier | TypeSpec | 0 | genericFunctions.go:84:21:84:28 | type parameter declaration | 0 | genericFunctions.go:84:21:84:21 | S | genericFunctions.go:84:26:84:28 | any | interface { } | +| genericFunctions.go:84:6:85:1 | type declaration specifier | TypeSpec | 0 | genericFunctions.go:84:21:84:28 | type parameter declaration | 1 | genericFunctions.go:84:24:84:24 | T | genericFunctions.go:84:26:84:28 | any | interface { } | +| genericFunctions.go:111:6:113:1 | type declaration specifier | TypeSpec | 0 | genericFunctions.go:111:14:111:18 | type parameter declaration | 0 | genericFunctions.go:111:14:111:14 | S | genericFunctions.go:111:16:111:18 | any | interface { } | +| genericFunctions.go:115:6:117:1 | type declaration specifier | TypeSpec | 0 | genericFunctions.go:115:11:115:15 | type parameter declaration | 0 | genericFunctions.go:115:11:115:11 | T | genericFunctions.go:115:13:115:15 | any | interface { } | +| genericFunctions.go:124:6:126:1 | type declaration specifier | TypeSpec | 0 | genericFunctions.go:124:21:124:28 | type parameter declaration | 0 | genericFunctions.go:124:21:124:24 | Edge | genericFunctions.go:124:26:124:28 | any | interface { } | +| genericFunctions.go:128:6:130:1 | type declaration specifier | TypeSpec | 0 | genericFunctions.go:128:21:128:28 | type parameter declaration | 0 | genericFunctions.go:128:21:128:24 | Node | genericFunctions.go:128:26:128:28 | any | interface { } | +| genericFunctions.go:132:6:132:73 | type declaration specifier | TypeSpec | 0 | genericFunctions.go:132:12:132:36 | type parameter declaration | 0 | genericFunctions.go:132:12:132:15 | Node | genericFunctions.go:132:17:132:36 | generic type instantiation expression | NodeConstraint | +| genericFunctions.go:132:6:132:73 | type declaration specifier | TypeSpec | 1 | genericFunctions.go:132:39:132:63 | type parameter declaration | 0 | genericFunctions.go:132:39:132:42 | Edge | genericFunctions.go:132:44:132:63 | generic type instantiation expression | EdgeConstraint | +| genericFunctions.go:134:1:136:1 | function declaration | FuncDecl | 0 | genericFunctions.go:134:10:134:34 | type parameter declaration | 0 | genericFunctions.go:134:10:134:13 | Node | genericFunctions.go:134:15:134:34 | generic type instantiation expression | NodeConstraint | +| genericFunctions.go:134:1:136:1 | function declaration | FuncDecl | 1 | genericFunctions.go:134:37:134:61 | type parameter declaration | 0 | genericFunctions.go:134:37:134:40 | Edge | genericFunctions.go:134:42:134:61 | generic type instantiation expression | EdgeConstraint | diff --git a/ql/test/library-tests/semmle/go/Function/go.mod b/ql/test/library-tests/semmle/go/Function/go.mod new file mode 100644 index 00000000000..85d3db5c5a3 --- /dev/null +++ b/ql/test/library-tests/semmle/go/Function/go.mod @@ -0,0 +1,6 @@ +module codeql-go-tests/function + +go 1.18 + +require github.com/anotherpkg v0.0.0-20200203000000-0000000000000 + diff --git a/ql/test/library-tests/semmle/go/Function/vendor/github.com/anotherpkg/README.md b/ql/test/library-tests/semmle/go/Function/vendor/github.com/anotherpkg/README.md new file mode 100644 index 00000000000..8dd7c7f5401 --- /dev/null +++ b/ql/test/library-tests/semmle/go/Function/vendor/github.com/anotherpkg/README.md @@ -0,0 +1 @@ +This is a simple stub package, strictly for use in query testing. diff --git a/ql/test/library-tests/semmle/go/Function/vendor/github.com/anotherpkg/anotherpkg.go b/ql/test/library-tests/semmle/go/Function/vendor/github.com/anotherpkg/anotherpkg.go new file mode 100644 index 00000000000..6ac64bba2c6 --- /dev/null +++ b/ql/test/library-tests/semmle/go/Function/vendor/github.com/anotherpkg/anotherpkg.go @@ -0,0 +1,6 @@ +package anotherpkg + +func GenericFunctionInAnotherPackage[T any](t T) T { + var r T + return r +} diff --git a/ql/test/library-tests/semmle/go/Function/vendor/modules.txt b/ql/test/library-tests/semmle/go/Function/vendor/modules.txt new file mode 100644 index 00000000000..86277b3b019 --- /dev/null +++ b/ql/test/library-tests/semmle/go/Function/vendor/modules.txt @@ -0,0 +1,3 @@ +# github.com/anotherpkg v0.0.0-20200203000000-0000000000000 +## explicit; go 1.18 +github.com/anotherpkg From ac081dc47a938a83da4f45f90a6a7389d1d6b3e2 Mon Sep 17 00:00:00 2001 From: Owen Mansel-Chan Date: Wed, 27 Apr 2022 16:23:02 +0100 Subject: [PATCH 41/49] Make TypeParamParent map global --- extractor/extractor.go | 46 ++++++++++++++++++++++++++---------- extractor/trap/trapwriter.go | 2 -- 2 files changed, 33 insertions(+), 15 deletions(-) diff --git a/extractor/extractor.go b/extractor/extractor.go index 7f7f1cae677..c67d1109a9b 100644 --- a/extractor/extractor.go +++ b/extractor/extractor.go @@ -29,6 +29,7 @@ import ( ) var MaxGoRoutines int +var typeParamParent map[*types.TypeParam]types.Object = make(map[*types.TypeParam]types.Object) func init() { // this sets the number of threads that the Go runtime will spawn; this is separate @@ -365,13 +366,13 @@ 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(tw, funcObj.Type().(*types.Signature).TypeParams(), lbl) - populateTypeParamParents(tw, funcObj.Type().(*types.Signature).RecvTypeParams(), lbl) + populateTypeParamParents(tw, funcObj.Type().(*types.Signature).TypeParams(), obj) + populateTypeParamParents(tw, funcObj.Type().(*types.Signature).RecvTypeParams(), obj) } // Populate type parameter parents for named types. if typeNameObj, ok := obj.(*types.TypeName); ok { if tp, ok := typeNameObj.Type().(*types.Named); ok { - populateTypeParamParents(tw, tp.TypeParams(), lbl) + populateTypeParamParents(tw, tp.TypeParams(), obj) } } extractObject(tw, obj, lbl) @@ -397,8 +398,8 @@ 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(tw, meth.Type().(*types.Signature).TypeParams(), methlbl) - populateTypeParamParents(tw, meth.Type().(*types.Signature).RecvTypeParams(), methlbl) + populateTypeParamParents(tw, meth.Type().(*types.Signature).TypeParams(), meth) + populateTypeParamParents(tw, meth.Type().(*types.Signature).RecvTypeParams(), meth) extractObject(tw, meth, methlbl) } @@ -1099,6 +1100,7 @@ func extractExprs(tw *trap.Writer, exprs []ast.Expr, parent trap.Label, idx int, func extractTypeOf(tw *trap.Writer, expr ast.Expr, lbl trap.Label) { tp := typeOf(tw, expr) if tp != nil { + // log.Printf("Extracting type of expr %v", expr) tplbl := extractType(tw, tp) dbscheme.TypeOfTable.Emit(tw, lbl, tplbl) } @@ -1574,10 +1576,7 @@ func extractType(tw *trap.Writer, tp types.Type) trap.Label { } case *types.TypeParam: kind = dbscheme.TypeParamType.Index() - parentlbl, exists := tw.TypeParamParent[tp] - if !exists { - log.Fatalf("Parent of type parameter does not exist: %s", tp.String()) - } + parentlbl := getTypeParamParentLabel(tw, tp) constraintLabel := extractType(tw, tp.Constraint()) dbscheme.TypeParamTable.Emit(tw, lbl, tp.Obj().Name(), constraintLabel, parentlbl, tp.Index()) case *types.Union: @@ -1718,7 +1717,7 @@ func getTypeLabel(tw *trap.Writer, tp types.Type) (trap.Label, bool) { } lbl = tw.Labeler.GlobalID(fmt.Sprintf("{%s};namedtype", entitylbl)) case *types.TypeParam: - parentlbl := tw.TypeParamParent[tp] + parentlbl := getTypeParamParentLabel(tw, tp) lbl = tw.Labeler.GlobalID(fmt.Sprintf("{%v},%s;typeparamtype", parentlbl, tp.Obj().Name())) case *types.Union: var b strings.Builder @@ -1903,11 +1902,11 @@ func extractTypeParamDecls(tw *trap.Writer, fields *ast.FieldList, parent trap.L } } -// populateTypeParamParents sets `parentlbl` as the parent of the elements of `typeparams` -func populateTypeParamParents(tw *trap.Writer, typeparams *types.TypeParamList, parentlbl trap.Label) { +// populateTypeParamParents sets `parent` as the parent of the elements of `typeparams` +func populateTypeParamParents(tw *trap.Writer, typeparams *types.TypeParamList, parent types.Object) { if typeparams != nil { for idx := 0; idx < typeparams.Len(); idx++ { - tw.TypeParamParent[typeparams.At(idx)] = parentlbl + setTypeParamParent(typeparams.At(idx), parent) } } } @@ -1999,3 +1998,24 @@ func trackInstantiatedStructFields(tw *trap.Writer, tp, origintp *types.Named) { } } } + +func getTypeParamParentLabel(tw *trap.Writer, tp *types.TypeParam) trap.Label { + parent, 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 { + log.Fatalf("getTypeLabel() called for parent of type parameter %s", tp.String()) + return trap.InvalidLabel + }) + return parentlbl +} + +func setTypeParamParent(tp *types.TypeParam, newobj types.Object) { + obj, 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) + } +} diff --git a/extractor/trap/trapwriter.go b/extractor/trap/trapwriter.go index 713cb341827..4c0911e24d7 100644 --- a/extractor/trap/trapwriter.go +++ b/extractor/trap/trapwriter.go @@ -26,7 +26,6 @@ type Writer struct { trapFilePath string Package *packages.Package TypesOverride map[ast.Expr]types.Type - TypeParamParent map[*types.TypeParam]Label ObjectsOverride map[types.Object]types.Object } @@ -66,7 +65,6 @@ func NewWriter(path string, pkg *packages.Package) (*Writer, error) { trapFilePath, pkg, make(map[ast.Expr]types.Type), - make(map[*types.TypeParam]Label), make(map[types.Object]types.Object), } tw.Labeler = newLabeler(tw) From d098bdc7f8082fa3a24ab432cd02cc22c920070c Mon Sep 17 00:00:00 2001 From: Owen Mansel-Chan Date: Fri, 29 Apr 2022 23:11:56 +0100 Subject: [PATCH 42/49] Reintroduce noinlined predicate to fix performance --- ql/lib/semmle/go/Types.qll | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/ql/lib/semmle/go/Types.qll b/ql/lib/semmle/go/Types.qll index 08e3c43d0f7..65399883a50 100644 --- a/ql/lib/semmle/go/Types.qll +++ b/ql/lib/semmle/go/Types.qll @@ -86,7 +86,7 @@ class Type extends @type { tslit.includesType(this) ) and ( - not i.hasMethod(_, _) + hasNoMethods(i) or this.hasMethod(getExampleMethodName(i), _) and forall(string m, SignatureType t | i.hasMethod(m, t) | this.hasMethod(m, t)) @@ -708,7 +708,7 @@ class TypeSetLiteralType extends @typesetliteraltype, CompositeType { InterfaceType getInterfaceType() { this = result.getDirectlyEmbeddedTypeSetLiteral(0) and not exists(result.getDirectlyEmbeddedTypeSetLiteral(1)) and - not result.hasMethod(_, _) and + hasNoMethods(result) and not exists(result.getADirectlyEmbeddedInterface()) } @@ -834,6 +834,10 @@ class InterfaceType extends @interfacetype, CompositeType { override string toString() { result = "interface type" } } +// This predicate is needed for performance reasons. +pragma[noinline] +private predicate hasNoMethods(InterfaceType i) { not i.hasMethod(_, _) } + /** * A basic interface type. * @@ -857,7 +861,7 @@ class BasicInterfaceType extends InterfaceType { * `comparable`. This is done by extending `BasicInterfaceType`. */ class EmptyInterfaceType extends BasicInterfaceType { - EmptyInterfaceType() { not this.hasMethod(_, _) } + EmptyInterfaceType() { hasNoMethods(this) } } /** From 3c4b5202e3ed65e8546aeaf02d03a0a8e91252df Mon Sep 17 00:00:00 2001 From: Owen Mansel-Chan Date: Tue, 3 May 2022 11:25:33 +0100 Subject: [PATCH 43/49] Fix type aliases for instantiated generic types --- extractor/extractor.go | 6 ++++-- .../semmle/go/Types/GenericTypeInstantiationExpr.expected | 1 + .../library-tests/semmle/go/Types/QualifiedNames.expected | 1 + ql/test/library-tests/semmle/go/Types/Types.expected | 1 + ql/test/library-tests/semmle/go/Types/generic.go | 5 +++++ 5 files changed, 12 insertions(+), 2 deletions(-) diff --git a/extractor/extractor.go b/extractor/extractor.go index c67d1109a9b..d79a6db36de 100644 --- a/extractor/extractor.go +++ b/extractor/extractor.go @@ -369,8 +369,10 @@ func extractObjects(tw *trap.Writer, scope *types.Scope, scopeLabel trap.Label) populateTypeParamParents(tw, funcObj.Type().(*types.Signature).TypeParams(), obj) populateTypeParamParents(tw, funcObj.Type().(*types.Signature).RecvTypeParams(), obj) } - // Populate type parameter parents for named types. - if typeNameObj, ok := obj.(*types.TypeName); ok { + // Populate type parameter parents for named types. Note that we + // skip type aliases as the original type should be the parent + // of any type parameters. + if typeNameObj, ok := obj.(*types.TypeName); ok && !typeNameObj.IsAlias() { if tp, ok := typeNameObj.Type().(*types.Named); ok { populateTypeParamParents(tw, tp.TypeParams(), obj) } diff --git a/ql/test/library-tests/semmle/go/Types/GenericTypeInstantiationExpr.expected b/ql/test/library-tests/semmle/go/Types/GenericTypeInstantiationExpr.expected index d22baa21da7..a6941dc84a2 100644 --- a/ql/test/library-tests/semmle/go/Types/GenericTypeInstantiationExpr.expected +++ b/ql/test/library-tests/semmle/go/Types/GenericTypeInstantiationExpr.expected @@ -16,3 +16,4 @@ | generic.go:62:12:62:26 | generic type instantiation expression | generic.go:62:12:62:23 | GenericNamed | 0 | generic.go:62:25:62:25 | U | | generic.go:70:42:70:64 | generic type instantiation expression | generic.go:70:42:70:57 | GenericInterface | 0 | generic.go:70:59:70:63 | int32 | | generic.go:74:41:74:62 | generic type instantiation expression | generic.go:74:41:74:54 | GenericStruct1 | 0 | generic.go:74:56:74:61 | string | +| generic.go:82:18:82:34 | generic type instantiation expression | generic.go:82:18:82:29 | GenericArray | 0 | generic.go:82:31:82:33 | int | diff --git a/ql/test/library-tests/semmle/go/Types/QualifiedNames.expected b/ql/test/library-tests/semmle/go/Types/QualifiedNames.expected index d5ea0214f9f..e8b335f5de7 100644 --- a/ql/test/library-tests/semmle/go/Types/QualifiedNames.expected +++ b/ql/test/library-tests/semmle/go/Types/QualifiedNames.expected @@ -19,6 +19,7 @@ | GenericMap2 | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types.GenericMap2 | | GenericNamed | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types.GenericNamed | | GenericPointer | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types.GenericPointer | +| GenericSignature | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types.GenericSignature | | GenericSlice | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types.GenericSlice | | GenericStruct1 | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types.GenericStruct1 | | GenericStruct2 | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types.GenericStruct2 | diff --git a/ql/test/library-tests/semmle/go/Types/Types.expected b/ql/test/library-tests/semmle/go/Types/Types.expected index 1eed45e95e0..e0dfa3791dd 100644 --- a/ql/test/library-tests/semmle/go/Types/Types.expected +++ b/ql/test/library-tests/semmle/go/Types/Types.expected @@ -19,6 +19,7 @@ | GenericMap2 | GenericMap2 | | GenericNamed | GenericNamed | | GenericPointer | GenericPointer | +| GenericSignature | GenericSignature | | GenericSlice | GenericSlice | | GenericStruct1 | GenericStruct1 | | GenericStruct2 | GenericStruct2 | diff --git a/ql/test/library-tests/semmle/go/Types/generic.go b/ql/test/library-tests/semmle/go/Types/generic.go index 70381bb2890..66c739f7061 100644 --- a/ql/test/library-tests/semmle/go/Types/generic.go +++ b/ql/test/library-tests/semmle/go/Types/generic.go @@ -78,3 +78,8 @@ func accessFieldsOfInstantiatedStruct(x GenericStruct1[string]) { _ = x.sliceField _ = x.mapField } + +type TypeAlias = GenericArray[int] + +type GenericSignature[T any] func(t T) T +type GenericSignatureAlias = GenericSignature[string] From d330033908064c39665ee498a1c6b6fd5bc56cf4 Mon Sep 17 00:00:00 2001 From: Chris Smowton Date: Tue, 10 May 2022 14:30:51 +0100 Subject: [PATCH 44/49] Make objects-map-changed warning non-fatal --- extractor/extractor.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/extractor/extractor.go b/extractor/extractor.go index d79a6db36de..075cb3175d3 100644 --- a/extractor/extractor.go +++ b/extractor/extractor.go @@ -466,8 +466,7 @@ func extractObjectTypes(tw *trap.Writer) { } changed = tw.ForEachObject(emitObjectType) if changed { - // TODO: Make this non-fatal before commiting - log.Fatalf("Warning: more objects were labeled while emitted object types") + log.Printf("Warning: more objects were labeled while emitted object types") } } From 3129c3dd693edeffad9ea45bfdb23e72ce91e744 Mon Sep 17 00:00:00 2001 From: Chris Smowton Date: Tue, 10 May 2022 14:35:10 +0100 Subject: [PATCH 45/49] Remove commented-out debug code --- extractor/extractor.go | 1 - 1 file changed, 1 deletion(-) diff --git a/extractor/extractor.go b/extractor/extractor.go index 075cb3175d3..1be2bfef224 100644 --- a/extractor/extractor.go +++ b/extractor/extractor.go @@ -1101,7 +1101,6 @@ func extractExprs(tw *trap.Writer, exprs []ast.Expr, parent trap.Label, idx int, func extractTypeOf(tw *trap.Writer, expr ast.Expr, lbl trap.Label) { tp := typeOf(tw, expr) if tp != nil { - // log.Printf("Extracting type of expr %v", expr) tplbl := extractType(tw, tp) dbscheme.TypeOfTable.Emit(tw, lbl, tplbl) } From c0fbd031336db4fcec4e45d67ffd54a9dd27007d Mon Sep 17 00:00:00 2001 From: Chris Smowton Date: Tue, 10 May 2022 14:42:38 +0100 Subject: [PATCH 46/49] Add qldoc for `getTermInIntersection` --- ql/lib/semmle/go/Types.qll | 3 +++ 1 file changed, 3 insertions(+) diff --git a/ql/lib/semmle/go/Types.qll b/ql/lib/semmle/go/Types.qll index 65399883a50..8ec3978e6f1 100644 --- a/ql/lib/semmle/go/Types.qll +++ b/ql/lib/semmle/go/Types.qll @@ -668,6 +668,9 @@ private TypeSetTerm getIntersection(TypeSetTerm term1, TypeSetTerm term2) { if term1.hasTilde() then result = term2 else result = term1 } +/** + * Gets a term in the intersection of type-set literals `a` and `b`. + */ TypeSetTerm getTermInIntersection(TypeSetLiteralType a, TypeSetLiteralType b) { result = getIntersection(a.getATerm(), b.getATerm()) } From 7530943e07c529b413471f5b920f5fc1f1b09b12 Mon Sep 17 00:00:00 2001 From: Chris Smowton Date: Wed, 11 May 2022 10:42:58 +0100 Subject: [PATCH 47/49] Add change note announcing generics support --- ql/lib/change-notes/2022-05-11-generics-support.md | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 ql/lib/change-notes/2022-05-11-generics-support.md diff --git a/ql/lib/change-notes/2022-05-11-generics-support.md b/ql/lib/change-notes/2022-05-11-generics-support.md new file mode 100644 index 00000000000..b52c6aa4c10 --- /dev/null +++ b/ql/lib/change-notes/2022-05-11-generics-support.md @@ -0,0 +1,4 @@ +--- +category: feature +--- +* Go 1.18 generics are now extracted and can be explored using the new CodeQL classes `TypeParamDecl`, `GenericFunctionInstantiationExpr`, `GenericTypeInstantiationExpr`, `TypeSetTerm` and `TypeSetLiteralType`, as well as using new predicates defined on the existing `InterfaceType`. Class- and predicate-level documentation can be found in the [Go CodeQL library reference](https://codeql.github.com/codeql-standard-libraries/go/). From 72022e65d5b454d519cc10558502722aa27ed281 Mon Sep 17 00:00:00 2001 From: Chris Smowton Date: Wed, 11 May 2022 10:46:16 +0100 Subject: [PATCH 48/49] Copyedit --- ql/lib/change-notes/2022-05-11-generics-support.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ql/lib/change-notes/2022-05-11-generics-support.md b/ql/lib/change-notes/2022-05-11-generics-support.md index b52c6aa4c10..295800fdcdf 100644 --- a/ql/lib/change-notes/2022-05-11-generics-support.md +++ b/ql/lib/change-notes/2022-05-11-generics-support.md @@ -1,4 +1,4 @@ --- category: feature --- -* Go 1.18 generics are now extracted and can be explored using the new CodeQL classes `TypeParamDecl`, `GenericFunctionInstantiationExpr`, `GenericTypeInstantiationExpr`, `TypeSetTerm` and `TypeSetLiteralType`, as well as using new predicates defined on the existing `InterfaceType`. Class- and predicate-level documentation can be found in the [Go CodeQL library reference](https://codeql.github.com/codeql-standard-libraries/go/). +* Go 1.18 generics are now extracted and can be explored using the new CodeQL classes `TypeParamDecl`, `GenericFunctionInstantiationExpr`, `GenericTypeInstantiationExpr`, `TypeSetTerm`, and `TypeSetLiteralType`, as well as using new predicates defined on the existing `InterfaceType`. Class- and predicate-level documentation can be found in the [Go CodeQL library reference](https://codeql.github.com/codeql-standard-libraries/go/). From e68a727f9a3da01b5f452393936e43680b3646b2 Mon Sep 17 00:00:00 2001 From: Tom Hvitved Date: Thu, 12 May 2022 20:21:48 +0200 Subject: [PATCH 49/49] Drop redundant columns from `files` and `folders` relations in `xml.dbscheme` --- ql/lib/xml.dbscheme | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/ql/lib/xml.dbscheme b/ql/lib/xml.dbscheme index 8e909080bf3..aa2155a69e2 100644 --- a/ql/lib/xml.dbscheme +++ b/ql/lib/xml.dbscheme @@ -51,16 +51,12 @@ numlines( files( unique int id: @file, - string name: string ref, - string simple: string ref, - string ext: string ref, - int fromSource: int ref // deprecated + string name: string ref ); folders( unique int id: @folder, - string name: string ref, - string simple: string ref + string name: string ref ); @container = @folder | @file