From 1e082016321db94c279dda2e2a9f84eb76231b35 Mon Sep 17 00:00:00 2001 From: Owen Mansel-Chan Date: Tue, 29 Mar 2022 12:30:29 +0100 Subject: [PATCH] Extract type param declarations --- extractor/dbscheme/tables.go | 17 +++++++++-- extractor/extractor.go | 30 ++++++++++++++++++++ ql/lib/go.dbscheme | 12 +++++--- ql/lib/semmle/go/AST.qll | 18 ++++++++++++ ql/lib/semmle/go/Decls.qll | 55 ++++++++++++++++++++++++++++++++++-- ql/lib/semmle/go/Expr.qll | 4 +++ 6 files changed, 128 insertions(+), 8 deletions(-) diff --git a/extractor/dbscheme/tables.go b/extractor/dbscheme/tables.go index 6bf253a103f..6325c6fd2a0 100644 --- a/extractor/dbscheme/tables.go +++ b/extractor/dbscheme/tables.go @@ -275,6 +275,9 @@ var StmtParentType = NewUnionType("@stmtparent", NodeType) // DeclParentType is the type of AST nodes that can have declarations as children var DeclParentType = NewUnionType("@declparent", NodeType) +// TypeParamDeclParentType is the type of AST nodes that can have type parameter declarations as children +var TypeParamDeclParentType = NewUnionType("@typeparamdeclparent", NodeType) + // FuncDefType is the type of AST nodes that define functions, that is, function // declarations and function literals var FuncDefType = NewUnionType("@funcdef", StmtParentType, ExprParentType) @@ -309,6 +312,9 @@ var StmtType = NewPrimaryKeyType("@stmt", ExprParentType, StmtParentType) // DeclType is the type of declaration AST nodes var DeclType = NewPrimaryKeyType("@decl", ExprParentType, StmtParentType, FieldParentType) +// TypeParamDeclType is the type of type parameter declaration AST nodes +var TypeParamDeclType = NewPrimaryKeyType("@typeparamdecl", DocumentableType, ExprParentType) + // SpecType is the type of spec AST nodes var SpecType = NewPrimaryKeyType("@spec", ExprParentType, DocumentableType) @@ -650,7 +656,7 @@ var TypeDeclType = DeclKind.NewBranch("@typedecl", GenDeclType) var VarDeclType = DeclKind.NewBranch("@vardecl", GenDeclType) // FuncDeclType is the type of function declaration AST nodes -var FuncDeclType = DeclKind.NewBranch("@funcdecl", DocumentableType, FuncDefType) +var FuncDeclType = DeclKind.NewBranch("@funcdecl", DocumentableType, FuncDefType, TypeParamDeclParentType) // SpecKind is a case type for distinguishing different kinds of declaration specification nodes var SpecKind = NewCaseType(SpecType, "kind") @@ -662,7 +668,7 @@ var ImportSpecType = SpecKind.NewBranch("@importspec") var ValueSpecType = SpecKind.NewBranch("@valuespec") // TypeSpecType is the type of type declaration specification nodes -var TypeSpecType = NewUnionType("@typespec") +var TypeSpecType = NewUnionType("@typespec", TypeParamDeclParentType) // TypeDefSpecType is the type of type declaration specification nodes corresponding to type definitions var TypeDefSpecType = SpecKind.NewBranch("@typedefspec", TypeSpecType) @@ -991,6 +997,13 @@ var FieldsTable = NewTable("fields", IntColumn("idx"), ) +// TypeParamDeclsTable is the table defining type param declaration AST nodes +var TypeParamDeclsTable = NewTable("typeparamdecls", + EntityColumn(TypeParamDeclType, "id").Key(), + EntityColumn(TypeParamDeclParentType, "parent"), + IntColumn("idx"), +) + // StmtsTable is the table defining statement AST nodes var StmtsTable = NewTable("stmts", EntityColumn(StmtType, "id").Key(), diff --git a/extractor/extractor.go b/extractor/extractor.go index 29a272515b3..c5407d4cf7e 100644 --- a/extractor/extractor.go +++ b/extractor/extractor.go @@ -1336,6 +1336,12 @@ func extractDecl(tw *trap.Writer, decl ast.Decl, parent trap.Label, idx int) { extractExpr(tw, decl.Type, lbl, 1) extractStmt(tw, decl.Body, lbl, 2) extractDoc(tw, decl.Doc, lbl) + extractTypeParamDecls(tw, decl.Type.TypeParams, lbl) + + // Note that we currently don't extract any kind of declaration for + // receiver type parameters. There isn't an explicit declaration, but + // we could consider the index/indices of an IndexExpr/IndexListExpr + // receiver as declarations. default: log.Fatalf("unknown declaration of type %T", decl) } @@ -1377,6 +1383,7 @@ func extractSpec(tw *trap.Writer, spec ast.Spec, parent trap.Label, idx int) { kind = dbscheme.TypeDefSpecType.Index() } extractExpr(tw, spec.Name, lbl, 0) + extractTypeParamDecls(tw, spec.TypeParams, lbl) extractExpr(tw, spec.Type, lbl, 1) extractDoc(tw, spec.Doc, lbl) } @@ -1798,3 +1805,26 @@ func flattenBinaryExprTree(tw *trap.Writer, e ast.Expr, parent trap.Label, idx i } return idx } + +func extractTypeParamDecls(tw *trap.Writer, fields *ast.FieldList, parent trap.Label) { + if fields == nil || fields.List == nil { + return + } + + overrideTypesOfTypeSetLiterals(tw, fields) + + idx := 0 + for _, field := range fields.List { + lbl := tw.Labeler.LocalID(field) + dbscheme.TypeParamDeclsTable.Emit(tw, lbl, parent, idx) + extractNodeLocation(tw, field, lbl) + if field.Names != nil { + for i, name := range field.Names { + extractExpr(tw, name, lbl, i+1) + } + } + extractExpr(tw, field.Type, lbl, 0) + extractDoc(tw, field.Doc, lbl) + idx += 1 + } +} diff --git a/ql/lib/go.dbscheme b/ql/lib/go.dbscheme index 4c567b898b8..3110ca88d98 100644 --- a/ql/lib/go.dbscheme +++ b/ql/lib/go.dbscheme @@ -157,6 +157,8 @@ constvalues(unique int expr: @expr ref, string value: string ref, string exact: fields(unique int id: @field, int parent: @fieldparent ref, int idx: int ref); +typeparamdecls(unique int id: @typeparamdecl, int parent: @typeparamdeclparent ref, int idx: int ref); + #keyset[parent, idx] stmts(unique int id: @stmt, int kind: int ref, int parent: @stmtparent ref, int idx: int ref); @@ -231,12 +233,12 @@ typeparam(unique int tp: @typeparamtype ref, string name: string ref, int bound: @locatable = @xmllocatable | @node | @localscope; -@node = @documentable | @exprparent | @modexprparent | @fieldparent | @stmtparent | @declparent | @scopenode - | @comment_group | @comment; +@node = @documentable | @exprparent | @modexprparent | @fieldparent | @stmtparent | @declparent | @typeparamdeclparent + | @scopenode | @comment_group | @comment; -@documentable = @file | @field | @spec | @gendecl | @funcdecl | @modexpr; +@documentable = @file | @field | @typeparamdecl | @spec | @gendecl | @funcdecl | @modexpr; -@exprparent = @funcdef | @file | @expr | @field | @stmt | @decl | @spec; +@exprparent = @funcdef | @file | @expr | @field | @stmt | @decl | @typeparamdecl | @spec; @modexprparent = @file | @modexpr; @@ -246,6 +248,8 @@ typeparam(unique int tp: @typeparamtype ref, string name: string ref, int bound: @declparent = @file | @declstmt; +@typeparamdeclparent = @functypeexpr | @typespec; + @funcdef = @funclit | @funcdecl; @scopenode = @file | @functypeexpr | @blockstmt | @ifstmt | @caseclause | @switchstmt | @commclause | @loopstmt; diff --git a/ql/lib/semmle/go/AST.qll b/ql/lib/semmle/go/AST.qll index 6ce205aefae..6c632af5e9b 100644 --- a/ql/lib/semmle/go/AST.qll +++ b/ql/lib/semmle/go/AST.qll @@ -216,6 +216,24 @@ class FieldParent extends @fieldparent, AstNode { int getNumFields() { result = count(getAField()) } } +/** + * An AST node whose children include type parameter declarations. + */ +class TypeParamDeclParent extends @typeparamdeclparent, AstNode { + /** + * Gets the `i`th type parameter declaration of this node. + * + * Note that the precise indices of type parameters are considered an implementation detail + * and are subject to change without notice. + */ + TypeParamDecl getTypeParameterDecl(int i) { typeparamdecls(result, this, i) } + + /** + * Gets a child field of this node in the AST. + */ + TypeParamDecl getATypeParameterDecl() { result = getTypeParameterDecl(_) } +} + /** * An AST node which may induce a scope. * diff --git a/ql/lib/semmle/go/Decls.qll b/ql/lib/semmle/go/Decls.qll index 3e3d5d1b831..0623aa1cc45 100644 --- a/ql/lib/semmle/go/Decls.qll +++ b/ql/lib/semmle/go/Decls.qll @@ -146,7 +146,7 @@ class FuncDef extends @funcdef, StmtParent, ExprParent { /** * A function declaration. */ -class FuncDecl extends @funcdecl, Decl, Documentable, FuncDef { +class FuncDecl extends @funcdecl, Decl, Documentable, FuncDef, TypeParamDeclParent { /** Gets the identifier denoting the name of this function. */ Ident getNameExpr() { result = this.getChildExpr(0) } @@ -376,7 +376,7 @@ class ValueSpec extends @valuespec, Spec { * ) * ``` */ -class TypeSpec extends @typespec, Spec { +class TypeSpec extends @typespec, Spec, TypeParamDeclParent { /** Gets the identifier denoting the name of the declared type. */ Ident getNameExpr() { result = this.getChildExpr(0) } @@ -562,6 +562,57 @@ class ResultVariableDecl extends ParameterOrResultDecl { override string getAPrimaryQlClass() { result = "ResultVariableDecl" } } +/** + * A type parameter declaration in a type specification. + */ +class TypeParamDecl extends @typeparamdecl, Documentable, ExprParent { + TypeParamDecl() { typeparamdecls(this, _, _) } + + /** + * Gets the expression representing the type constraint of the type + * parameters in this declaration. + * + * If you want the type constraint type itself then use `getTypeConstraint`, + * as that wraps type set literals with implicit interface types. + */ + Expr getTypeConstraintExpr() { result = this.getChildExpr(0) } + + /** + * Gets the type constraint of the type parameters in this declaration. + * + * If the type constraint is a type set literal then it will be wrapped + * with an implicit interface type. + */ + Type getTypeConstraint() { + exists(Type t | t = this.getTypeConstraintExpr().getType() | + if t instanceof TypeSetLiteralType + then result = t.(TypeSetLiteralType).getInterfaceType() + else result = t + ) + } + + /** + * Gets the expression representing the name of the `i`th type parameter + * in this declaration (0-based). + */ + Expr getNameExpr(int i) { + i >= 0 and + result = this.getChildExpr(i + 1) + } + + /** + * Gets the `i`th type parameter type in this declaration (0-based). + */ + TypeParamType getTypeParamType(int i) { + i >= 0 and + result = this.getNameExpr(i).getType() + } + + override string toString() { result = "type parameter declaration" } + + override string getAPrimaryQlClass() { result = "TypeParamDecl" } +} + /** * A method or embedding specification in an interface type expression. */ diff --git a/ql/lib/semmle/go/Expr.qll b/ql/lib/semmle/go/Expr.qll index 9b9d0617460..6268885f494 100644 --- a/ql/lib/semmle/go/Expr.qll +++ b/ql/lib/semmle/go/Expr.qll @@ -2096,6 +2096,10 @@ private predicate isTypeExprTopDown(Expr e) { or e = any(ParameterDecl pd).getTypeExpr() or + e = any(TypeParamDecl tpd).getTypeConstraintExpr() + or + e = any(TypeParamDecl tpd).getNameExpr(_) + or e = any(ReceiverDecl rd).getTypeExpr() or e = any(ResultVariableDecl rvd).getTypeExpr()