diff --git a/ql/src/semmle/go/Expr.qll b/ql/src/semmle/go/Expr.qll index 3d2cf956c11..522567d35a4 100644 --- a/ql/src/semmle/go/Expr.qll +++ b/ql/src/semmle/go/Expr.qll @@ -593,6 +593,29 @@ class SelectorExpr extends @selectorexpr, Expr { override string getAPrimaryQlClass() { result = "SelectorExpr" } } +/** + * A selector expression that refers to a promoted field. These selectors may implicitly + * address an embedded struct of their base type (e.g. the selector `x.field` may implicitly + * address `x.Embedded.field`). Note they may also explicitly address `field`; being a + * `PromotedFieldSelector` only indicates the addressed field may be promoted, not that it + * is promoted in this particular context. + */ +class PromotedFieldSelector extends SelectorExpr { + PromotedFieldSelector() { this.refersTo(any(PromotedField f)) } + + /** + * Gets the underlying struct type of this selector's base. Note because this selector + * addresses a promoted field, the addressed field may not directly occur in the returned + * struct type. + */ + StructType getSelectedStructType() { + exists(Type baseType | baseType = this.getBase().getType().getUnderlyingType() | + pragma[only_bind_into](result) = + [baseType, baseType.(PointerType).getBaseType().getUnderlyingType()] + ) + } +} + /** * An index expression, that is, a base expression followed by an index. * diff --git a/ql/src/semmle/go/Scopes.qll b/ql/src/semmle/go/Scopes.qll index e4328baccef..b3d6fef6c30 100644 --- a/ql/src/semmle/go/Scopes.qll +++ b/ql/src/semmle/go/Scopes.qll @@ -353,6 +353,15 @@ class Field extends Variable { } } +/** + * A field that belongs to a struct that may be embedded within another struct. + * + * When a selector addresses such a field, it is possible it is implicitly addressing a nested struct. + */ +class PromotedField extends Field { + PromotedField() { this = any(StructType t).getFieldOfEmbedded(_, _, _, _) } +} + /** A built-in or declared function. */ class Function extends ValueEntity, @functionobject { /** Gets a call to this function. */ diff --git a/ql/src/semmle/go/controlflow/ControlFlowGraphImpl.qll b/ql/src/semmle/go/controlflow/ControlFlowGraphImpl.qll index a8e6cf77e96..ad62b1dc67f 100644 --- a/ql/src/semmle/go/controlflow/ControlFlowGraphImpl.qll +++ b/ql/src/semmle/go/controlflow/ControlFlowGraphImpl.qll @@ -48,16 +48,9 @@ private predicate isCond(Expr e) { e = any(ParenExpr par | isCond(par)).getExpr() } -private StructType getSelectedStructType(PromotedFieldSelector e) { - exists(Type baseType | baseType = e.getBase().getType().getUnderlyingType() | - pragma[only_bind_into](result) = - [baseType, baseType.(PointerType).getBaseType().getUnderlyingType()] - ) -} - private predicate implicitFieldSelection(PromotedFieldSelector e, int i, Field implicitField) { exists(StructType baseType, PromotedField child | - baseType = getSelectedStructType(e) and + baseType = e.getSelectedStructType() and ( e.refersTo(child) or @@ -68,14 +61,6 @@ private predicate implicitFieldSelection(PromotedFieldSelector e, int i, Field i ) } -private class PromotedFieldSelector extends SelectorExpr { - PromotedFieldSelector() { this.refersTo(any(PromotedField f)) } -} - -private class PromotedField extends Field { - PromotedField() { this = any(StructType t).getFieldOfEmbedded(_, _, _, _) } -} - /** * A node in the intra-procedural control-flow graph of a Go function or file. * diff --git a/ql/src/semmle/go/controlflow/IR.qll b/ql/src/semmle/go/controlflow/IR.qll index 736c9aee6de..1bb7781db47 100644 --- a/ql/src/semmle/go/controlflow/IR.qll +++ b/ql/src/semmle/go/controlflow/IR.qll @@ -271,23 +271,8 @@ module IR { result = simpleSelectorBase(e) } - private class PromotedField extends Field { - PromotedField() { this = any(StructType t).getFieldOfEmbedded(_, _, _, _) } - } - - private class PromotedFieldSelector extends SelectorExpr { - PromotedFieldSelector() { this.refersTo(any(PromotedField f)) } - } - - private StructType getSelectedStructType(PromotedFieldSelector e) { - exists(Type baseType | baseType = e.getBase().getType().getUnderlyingType() | - pragma[only_bind_into](result) = - [baseType, baseType.(PointerType).getBaseType().getUnderlyingType()] - ) - } - private Instruction promotedFieldSelectorBase(PromotedFieldSelector se, Field field) { - exists(StructType baseStructType | baseStructType = getSelectedStructType(se) | + exists(StructType baseStructType | baseStructType = se.getSelectedStructType() | if field = baseStructType.getOwnField(_, _) then result = MkImplicitDeref(se.getBase())