diff --git a/.github/workflows/codeqltest.yml b/.github/workflows/codeqltest.yml index e728473f..54e2e4d0 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.1 uses: actions/setup-go@v1 with: - go-version: 1.17 + 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.17 + - name: Set up Go 1.18.1 uses: actions/setup-go@v1 with: - go-version: 1.17 + 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.17 + - name: Set up Go 1.18.1 uses: actions/setup-go@v1 with: - go-version: 1.17 + go-version: 1.18.1 id: go - name: Set up CodeQL CLI diff --git a/downgrades/90fa7836e0a239f69bbebffcf342e92c240d54bc/exprs.ql b/downgrades/90fa7836e0a239f69bbebffcf342e92c240d54bc/exprs.ql new file mode 100644 index 00000000..b4b7498e --- /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 00000000..8f168c8a --- /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 00000000..90fa7836 --- /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 00000000..0b02cc68 --- /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 00000000..a233cc28 --- /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 00000000..8f168c8a --- /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 00000000..d3e056be --- /dev/null +++ b/downgrades/qlpack.yml @@ -0,0 +1,4 @@ +name: codeql/go-downgrades +groups: go +downgrades: . +library: true diff --git a/extractor/dbscheme/tables.go b/extractor/dbscheme/tables.go index 1f7a31e9..63332aa3 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" ) @@ -274,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) @@ -308,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) @@ -371,9 +378,23 @@ 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 AST nodes for index expressions and generic type +// 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 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. 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 var SliceExpr = ExprKind.NewBranch("@sliceexpr") @@ -453,6 +474,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") @@ -635,7 +659,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") @@ -647,7 +671,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) @@ -661,6 +685,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") @@ -674,7 +701,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) @@ -701,7 +728,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) @@ -790,6 +817,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) @@ -831,6 +861,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") @@ -970,6 +1003,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(), @@ -1171,3 +1211,12 @@ var HasEllipsisTable = NewTable("has_ellipsis", 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 3fa29569..1be2bfef 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 @@ -109,9 +110,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) { @@ -138,7 +137,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) @@ -361,8 +360,23 @@ 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 { + // 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(), obj) + populateTypeParamParents(tw, funcObj.Type().(*types.Signature).RecvTypeParams(), obj) + } + // 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) + } + } extractObject(tw, obj, lbl) } @@ -378,10 +392,16 @@ 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 + 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 { + // 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(), meth) + populateTypeParamParents(tw, meth.Type().(*types.Signature).RecvTypeParams(), meth) extractObject(tw, meth, methlbl) } @@ -435,8 +455,30 @@ 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 { + log.Printf("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)) } @@ -583,7 +625,7 @@ func (extraction *Extraction) extractFile(ast *ast.File, pkg *packages.Package) extractFileNode(tw, ast) - tw.ForEachObject(extractObjectType) + extractObjectTypes(tw) extractNumLines(tw, path, ast) @@ -685,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] @@ -801,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) @@ -877,9 +920,43 @@ func extractExpr(tw *trap.Writer, expr ast.Expr, parent trap.Label, idx int) { if expr == nil { return } - kind = dbscheme.IndexExpr.Index() + 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) + case *ast.IndexListExpr: + if expr == nil { + return + } + 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) case *ast.SliceExpr: if expr == nil { return @@ -923,23 +1000,33 @@ 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() + flattenBinaryExprTree(tw, expr, lbl, 0) + } 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 @@ -966,6 +1053,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. + makeTypeSetLiteralsUnionTyped(tw, expr.Methods) extractFields(tw, expr.Methods, lbl, 0, 1) case *ast.MapType: if expr == nil { @@ -1009,7 +1099,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) @@ -1304,6 +1394,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) } @@ -1345,6 +1441,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) } @@ -1377,7 +1474,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) @@ -1399,10 +1498,19 @@ 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()) } + 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++ { @@ -1410,11 +1518,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 { @@ -1434,24 +1542,27 @@ 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) + trackInstantiatedStructFields(tw, tp, origintp) - 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) + // 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) extractMethod(tw, meth) } @@ -1463,6 +1574,21 @@ func extractType(tw *trap.Writer, tp types.Type) trap.Label { dbscheme.MethodHostsTable.Emit(tw, methlbl, lbl) } } + case *types.TypeParam: + kind = dbscheme.TypeParamType.Index() + parentlbl := getTypeParamParentLabel(tw, tp) + 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++ { + term := tp.Term(i) + tildeStr := "" + if term.Tilde() { + tildeStr = "~" + } + extractComponentType(tw, lbl, i, tildeStr, term.Type()) + } default: log.Fatalf("unexpected type %T", tp) } @@ -1522,6 +1648,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 @@ -1535,14 +1674,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(";") @@ -1568,14 +1707,33 @@ 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)) + case *types.TypeParam: + parentlbl := getTypeParamParentLabel(tw, 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++ { + 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) } tw.Labeler.TypeLabels[tp] = lbl } @@ -1658,3 +1816,206 @@ 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 { + if t == nil { + return false + } + 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`. 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 + } + 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 +} + +func extractTypeParamDecls(tw *trap.Writer, fields *ast.FieldList, parent trap.Label) { + if fields == nil || fields.List == nil { + return + } + + // 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 { + 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 + } +} + +// 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++ { + setTypeParamParent(typeparams.At(idx), parent) + } + } +} + +// 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 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 { + 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 +} + +// 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) + } + } +} + +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/labels.go b/extractor/trap/labels.go index 2176cdf9..1e9bd298 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() @@ -154,19 +154,17 @@ func (l *Labeler) ScopedObjectID(object types.Object, typelbl Label) (Label, boo } 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, typelbl) - label, _ = l.ReceiverObjectID(object, methlbl) - } + 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())) } @@ -176,6 +174,18 @@ func (l *Labeler) ScopedObjectID(object types.Object, typelbl Label) (Label, boo 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 @@ -210,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 diff --git a/extractor/trap/trapwriter.go b/extractor/trap/trapwriter.go index ff96defa..4c0911e2 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,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 + zip *gzip.Writer + w *bufio.Writer + file *os.File + Labeler *Labeler + path string + trapFilePath string + Package *packages.Package + TypesOverride map[ast.Expr]types.Type + ObjectsOverride map[types.Object]types.Object } func FileFor(path string) (string, error) { @@ -61,6 +64,8 @@ func NewWriter(path string, pkg *packages.Package) (*Writer, error) { path, trapFilePath, pkg, + make(map[ast.Expr]types.Type), + make(map[types.Object]types.Object), } tw.Labeler = newLabeler(tw) return tw, nil @@ -104,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 diff --git a/go.mod b/go.mod index 5d77091a..25e2d87d 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 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 00000000..295800fd --- /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/). diff --git a/ql/lib/go.dbscheme b/ql/lib/go.dbscheme index 8f168c8a..90fa7836 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); @@ -225,16 +227,20 @@ 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 | @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; @@ -244,6 +250,8 @@ variadic(int id: @signaturetype ref); @declparent = @file | @declstmt; +@typeparamdeclparent = @funcdecl | @typespec; + @funcdef = @funclit | @funcdecl; @scopenode = @file | @functypeexpr | @blockstmt | @ifstmt | @caseclause | @switchstmt | @commclause | @loopstmt; @@ -270,46 +278,49 @@ 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 = @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; @@ -430,6 +441,8 @@ case @object.kind of | 7 = @builtinfunctionobject | 8 = @labelobject; +@typeparamparentobject = @decltypeobject | @declfunctionobject; + @declobject = @decltypeobject | @declconstobject | @declvarobject | @declfunctionobject; @builtinobject = @builtintypeobject | @builtinconstobject | @builtinfunctionobject; @@ -476,18 +489,20 @@ 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 +| 39 = @typesetliteraltype; @basictype = @booltype | @numerictype | @stringtype | @literaltype | @invalidtype | @unsafepointertype; @@ -510,7 +525,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 | @typesetliteraltype; @containertype = @arraytype | @slicetype | @maptype | @chantype; diff --git a/ql/lib/semmle/go/AST.qll b/ql/lib/semmle/go/AST.qll index 6ce205ae..a9862ea3 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 = this.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 3e3d5d1b..0623aa1c 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 4bddc6fc..d32d8728 100644 --- a/ql/lib/semmle/go/Expr.qll +++ b/ql/lib/semmle/go/Expr.qll @@ -626,14 +626,21 @@ 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 { + IndexExpr() { not isTypeExprBottomUp(this.getChildExpr(0)) } + /** Gets the base of this index expression. */ Expr getBase() { result = this.getChildExpr(0) } @@ -647,6 +654,68 @@ 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 function 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 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. * @@ -770,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) { @@ -995,6 +1068,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. * @@ -1956,15 +2045,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 @typesetliteralexpr + 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()) } @@ -1989,6 +2093,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() @@ -2005,6 +2113,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() diff --git a/ql/lib/semmle/go/Types.qll b/ql/lib/semmle/go/Types.qll index abc7d9ed..8ec3978e 100644 --- a/ql/lib/semmle/go/Types.qll +++ b/ql/lib/semmle/go/Types.qll @@ -66,18 +66,75 @@ 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 this.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.includesType(this) + ) and + ( + hasNoMethods(i) + 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 + 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 = uif.getAnEmbeddedTypeSetLiteral().getATerm().getType() and + forall(TypeSetLiteralType tslit | tslit = uif.getAnEmbeddedTypeSetLiteral() | + intersectionType = tslit.getATerm().getType() + ) + | + intersectionType.implementsComparable() + ) + else uif.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())) @@ -306,6 +363,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. */ @@ -532,33 +609,269 @@ 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 TTypeSetTerm = + MkTypeSetTerm(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 TypeSetTerm extends TTypeSetTerm { + boolean tilde; + Type tp; + + TypeSetTerm() { + exists(TypeSetLiteralType tslit, int index | + this = MkTypeSetTerm(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 includesType(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 = "type set term" } +} + +private TypeSetTerm getIntersection(TypeSetTerm term1, TypeSetTerm term2) { + term1.getType() = term2.getType() and + 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()) +} + +/** + * 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. */ + TypeSetTerm getTerm(int i) { result = MkTypeSetTerm(this, i) } + + /** Gets a term in this type set literal. */ + 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) } + + /** + * Gets the interface type of which this type-set literal is the only + * element, if it exists. + * + * 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 + not exists(result.getDirectlyEmbeddedTypeSetLiteral(1)) and + hasNoMethods(result) and + not exists(result.getADirectlyEmbeddedInterface()) + } language[monotonicAggregates] override string pp() { - exists(string meth | + result = concat(TypeSetTerm 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) { + // 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 a directly embedded type with index `index`. + * + * `tp` (or its underlying type) is either a type set literal type or an + * interface type. + */ + 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 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 getADirectlyEmbeddedInterface() { + this.hasDirectlyEmbeddedType(_, 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.getADirectlyEmbeddedInterface() or + result = + this.getADirectlyEmbeddedInterface() + .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 indexes are not contiguous. + */ + TypeSetLiteralType getDirectlyEmbeddedTypeSetLiteral(int index) { + hasDirectlyEmbeddedType(index, result) + } + + /** + * Gets a type set literal of this interface type. + * + * This includes type set literals of embedded interfaces. + */ + TypeSetLiteralType getAnEmbeddedTypeSetLiteral() { + result = this.getDirectlyEmbeddedTypeSetLiteral(_) or + result = + getADirectlyEmbeddedInterface() + .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 { - EmptyInterfaceType() { not this.hasMethod(_, _) } +// This predicate is needed for performance reasons. +pragma[noinline] +private predicate hasNoMethods(InterfaceType i) { not i.hasMethod(_, _) } + +/** + * 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() { hasNoMethods(this) } +} + +/** + * The predeclared `comparable` type. + */ +class ComparableType extends NamedType { + ComparableType() { this.getName() = "comparable" } } /** A tuple type. */ @@ -693,12 +1006,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`. * diff --git a/ql/lib/semmle/go/controlflow/ControlFlowGraphImpl.qll b/ql/lib/semmle/go/controlflow/ControlFlowGraphImpl.qll index 743b8e4f..25137f75 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 } diff --git a/ql/lib/semmle/go/dataflow/internal/DataFlowNodes.qll b/ql/lib/semmle/go/dataflow/internal/DataFlowNodes.qll index 91e97270..80c94caf 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. */ diff --git a/ql/lib/upgrades/8f168c8af3fee9b57bcfce85bb2ab708a5e3c828/exprs.ql b/ql/lib/upgrades/8f168c8af3fee9b57bcfce85bb2ab708a5e3c828/exprs.ql new file mode 100644 index 00000000..2e62387d --- /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 00000000..90fa7836 --- /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 00000000..8f168c8a --- /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 00000000..7efff59c --- /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 00000000..51a005f8 --- /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 diff --git a/ql/lib/xml.dbscheme b/ql/lib/xml.dbscheme index 8e909080..aa2155a6 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 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 00000000..6528a0ae --- /dev/null +++ b/ql/test/library-tests/semmle/go/Function/GenericFunctionInstantiationExpr.expected @@ -0,0 +1,8 @@ +| 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/GenericFunctionInstantiationExpr.ql b/ql/test/library-tests/semmle/go/Function/GenericFunctionInstantiationExpr.ql new file mode 100644 index 00000000..db3da8f8 --- /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 00000000..661cc775 --- /dev/null +++ b/ql/test/library-tests/semmle/go/Function/TypeParamType.expected @@ -0,0 +1,15 @@ +| 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 00000000..a4167f8d --- /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 00000000..43d0a11d --- /dev/null +++ b/ql/test/library-tests/semmle/go/Function/genericFunctions.go @@ -0,0 +1,148 @@ +package main + +import "github.com/anotherpkg" + +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 +} + +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{} } + +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 00000000..9394357d --- /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 d3a16330..538b6ff3 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 @@ +| 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 new file mode 100644 index 00000000..ce72080b --- /dev/null +++ b/ql/test/library-tests/semmle/go/Function/getTypeParameter.expected @@ -0,0 +1,15 @@ +| 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/getTypeParameter.ql b/ql/test/library-tests/semmle/go/Function/getTypeParameter.ql new file mode 100644 index 00000000..d22b6f98 --- /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/Function/go.mod b/ql/test/library-tests/semmle/go/Function/go.mod new file mode 100644 index 00000000..85d3db5c --- /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 00000000..8dd7c7f5 --- /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 00000000..6ac64bba --- /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 00000000..86277b3b --- /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 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 18a29cc6..e635a046 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 6baf1c54..2ac146f2 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 97524ed3..acdddfe8 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 00000000..a6941dc8 --- /dev/null +++ b/ql/test/library-tests/semmle/go/Types/GenericTypeInstantiationExpr.expected @@ -0,0 +1,19 @@ +| 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 | +| 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/GenericTypeInstantiationExpr.ql b/ql/test/library-tests/semmle/go/Types/GenericTypeInstantiationExpr.ql new file mode 100644 index 00000000..fd7ddf99 --- /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 00000000..e69de29b 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 00000000..b73b7111 --- /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 aa600fe8..2b6dc32c 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 a30d9f89..2364eb29 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 415f31ef..24eac7ce 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 415f31ef..24eac7ce 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 b051d1b6..8b2406ec 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 3eda347c..e8b335f5 100644 --- a/ql/test/library-tests/semmle/go/Types/QualifiedNames.expected +++ b/ql/test/library-tests/semmle/go/Types/QualifiedNames.expected @@ -7,15 +7,36 @@ | 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 | +| 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 | +| 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 +46,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/SignatureType_getNumParameter.expected b/ql/test/library-tests/semmle/go/Types/SignatureType_getNumParameter.expected index 3142d257..843020b9 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 d09b6d21..9072be74 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/StructFields.expected b/ql/test/library-tests/semmle/go/Types/StructFields.expected index 9bf97529..5ae7f7e7 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 60f9edd5..e0dfa379 100644 --- a/ql/test/library-tests/semmle/go/Types/Types.expected +++ b/ql/test/library-tests/semmle/go/Types/Types.expected @@ -7,15 +7,36 @@ | 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 | +| GenericSignature | GenericSignature | +| 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 +46,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 00000000..66c739f7 --- /dev/null +++ b/ql/test/library-tests/semmle/go/Types/generic.go @@ -0,0 +1,85 @@ +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{} + +func callMethodOnInstantiatedInterface(x GenericInterface[int32]) int32 { + return x.GetT() +} + +func accessFieldsOfInstantiatedStruct(x GenericStruct1[string]) { + _ = x.valueField + _ = x.pointerField + _ = x.arrayField + _ = x.sliceField + _ = x.mapField +} + +type TypeAlias = GenericArray[int] + +type GenericSignature[T any] func(t T) T +type GenericSignatureAlias = GenericSignature[string] 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 00000000..40653aac --- /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 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 4e62ba42..b97b385a 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 c83ad251..7daed37d 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 00000000..264681ab --- /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 +} 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 00000000..e69de29b 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 00000000..881d262f --- /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 00000000..b8242051 --- /dev/null +++ b/ql/test/library-tests/semmle/go/dataflow/GenericFunctionsAndTypes/genericfunctions.go @@ -0,0 +1,68 @@ +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 genericSink3[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" + } + { + f := genericSource[int8] + s := f(2) + sink(s) // $ hasValueFlow="s" + } + { + s := genericIdentity(source()) + sink(s) // $ hasValueFlow="s" + } + { + s := genericIdentity[string](source()) + sink(s) // $ hasValueFlow="s" + } + { + f := genericIdentity[string] + s := f(source()) + sink(s) // $ hasValueFlow="s" + } + { + s := source() + genericSink1(x, s) + } + { + 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 new file mode 100644 index 00000000..4e2dc169 --- /dev/null +++ b/ql/test/library-tests/semmle/go/dataflow/GenericFunctionsAndTypes/generictypesandmethods.go @@ -0,0 +1,139 @@ +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]{""} + 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(), ""} + sink(gs2.Field1) // $ hasValueFlow="selection of Field1" + } + { + 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()} + sink(gs2.Field2) // $ hasValueFlow="selection of Field2" + } + { + 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" + } +} 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 05af1e46..2de1ce81 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 dcd3a2f1..e0d6b678 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 |