Refactor embedded field calculation to expose access chain

This allows us to reuse the embedded field calculation in the
logic for generating implicit field selection nodes.
This commit is contained in:
Sauyon Lee
2021-03-10 04:48:59 -08:00
committed by Owen Mansel-Chan
parent c192a255c5
commit e1b4867a19
11 changed files with 107 additions and 42 deletions

View File

@@ -375,39 +375,38 @@ class StructType extends @structtype, CompositeType {
this.hasOwnField(_, name, _, isEmbedded)
}
/**
* hasEmbeddedField holds if there is an embedded field at int `depth`, with either type `tp` or `tp`'s pointer type.
*/
private predicate hasEmbeddedField(Type tp, int depth) {
hasFieldOrMethodCand(_, tp, depth, true, false)
or
exists(PointerType embeddedPtr |
hasFieldOrMethodCand(_, embeddedPtr, depth, true, false) and
tp = embeddedPtr.getBaseType()
exists(Field f | this.hasFieldCand(_, f, depth, true) | tp = f.getType().getBaseType*())
}
/**
* getFieldOfEmbedded gets a field of `embeddedParent`, which is then embedded into this struct type.
*/
Field getFieldOfEmbedded(Field embeddedParent, string name, int depth, boolean isEmbedded) {
// embeddedParent is a field of 'this' at depth 'depth - 1'
this.hasFieldCand(_, embeddedParent, depth - 1, true) and
// embeddedParent's type has the result field
exists(StructType embeddedType |
embeddedType = embeddedParent.getType().getBaseType*().getUnderlyingType()
|
result = embeddedType.getOwnField(name, isEmbedded)
)
}
private predicate hasFieldOrMethodCand(
string name, Type tp, int depth, boolean isEmbedded, boolean isMethod
) {
hasOwnField(_, name, tp, isEmbedded) and depth = 0 and isMethod = false
private predicate hasFieldCand(string name, Field f, int depth, boolean isEmbedded) {
f = this.getOwnField(name, isEmbedded) and depth = 0
or
not hasOwnField(_, name, _, _) and
exists(Type embedded | hasEmbeddedField(embedded, depth - 1) |
embedded.getUnderlyingType().(StructType).hasOwnField(_, name, tp, isEmbedded) and
isMethod = false
or
exists(MethodDecl md | md.getReceiverType() = embedded |
name = md.getName() and
tp = md.getType()
) and
isEmbedded = false and
isMethod = true
)
not this.hasOwnField(_, name, _, _) and
f = this.getFieldOfEmbedded(_, name, depth, isEmbedded)
}
private predicate hasFieldOrMethod(string name, Type tp, boolean isMethod) {
exists(int mindepth |
mindepth = min(int depth | hasFieldOrMethodCand(name, _, depth, _, _)) and
hasFieldOrMethodCand(name, tp, mindepth, _, isMethod) and
(strictcount(getFieldCand(name, mindepth, _)) = 1 or isMethod = true)
private predicate hasMethodCand(string name, Method m, int depth) {
name = m.getName() and
exists(Type embedded | this.hasEmbeddedField(embedded, depth - 1) |
m.getReceiverType() = embedded
)
}
@@ -415,7 +414,12 @@ class StructType extends @structtype, CompositeType {
* Holds if this struct contains a field `name` with type `tp`, possibly inside a (nested)
* embedded field.
*/
predicate hasField(string name, Type tp) { hasFieldOrMethod(name, tp, false) }
predicate hasField(string name, Type tp) {
exists(int mindepth |
mindepth = min(int depth | this.hasFieldCand(name, _, depth, _)) and
tp = unique(Field f | f = this.getFieldCand(name, mindepth, _)).getType()
)
}
private Field getFieldCand(string name, int depth, boolean isEmbedded) {
result = this.getOwnField(name, isEmbedded) and depth = 0
@@ -441,7 +445,12 @@ class StructType extends @structtype, CompositeType {
strictcount(getFieldCand(name, depth, _)) = 1
}
override predicate hasMethod(string name, SignatureType tp) { hasFieldOrMethod(name, tp, true) }
override predicate hasMethod(string name, SignatureType tp) {
exists(int mindepth |
mindepth = min(int depth | this.hasMethodCand(name, _, depth)) and
tp = unique(Method m | this.hasMethodCand(name, m, mindepth)).getType()
)
}
language[monotonicAggregates]
override string pp() {

View File

@@ -48,6 +48,19 @@ private predicate isCond(Expr e) {
e = any(ParenExpr par | isCond(par)).getExpr()
}
private predicate implicitFieldSelection(SelectorExpr e, int i, Field implicitField) {
exists(StructType baseType, Field child |
baseType = e.getBase().getType().getBaseType*().getUnderlyingType() and
(
e.getSelector() = child.getAReference()
or
implicitFieldSelection(e, i + 1, child)
)
|
child = baseType.getFieldOfEmbedded(implicitField, _, i, _)
)
}
/**
* A node in the intra-procedural control-flow graph of a Go function or file.
*
@@ -309,20 +322,7 @@ newtype TControlFlowNode =
* represents an implicit dereference of it.
*/
MkImplicitFieldSelection(SelectorExpr e, int i, Field implicitField) {
exists(Type baseType, StructType baseStructType, Field eField, int minDepth |
eField.getAReference() = e.getSelector() and
baseType = e.getBase().getType().getUnderlyingType() and
baseStructType = [baseType, baseType.(PointerType).getBaseType().getUnderlyingType()] and
baseStructType.getFieldAtDepth(_, minDepth) = eField
|
baseStructType.getFieldAtDepth(_, i) = implicitField and
exists(Type implicitFieldType, StructType implicitFieldStructType |
implicitFieldType = implicitField.getType().getUnderlyingType() and
implicitFieldStructType =
[implicitFieldType, implicitFieldType.(PointerType).getBaseType().getUnderlyingType()] and
implicitFieldStructType.getFieldAtDepth(_, minDepth - i - 1) = eField
)
)
implicitFieldSelection(e, i, implicitField)
} or
/**
* A control-flow node that represents the start of the execution of a function or file.

View File

@@ -2,11 +2,16 @@
| cyclic.go:8:3:8:3 | u | package github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types |
| cyclic.go:9:2:9:2 | f | package github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types |
| cyclic.go:13:2:13:2 | t | package github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types |
| cyclic.go:17:2:17:2 | s | package github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types |
| depth.go:6:2:6:2 | b | package github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types |
| depth.go:7:2:7:2 | c | package github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types |
| depth.go:11:2:11:2 | f | package github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types |
| depth.go:15:2:15:2 | d | package github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types |
| depth.go:19:2:19:2 | f | package github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types |
| embedded.go:4:2:4:2 | A | package github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types |
| 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 |
| 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 |

View File

@@ -5,6 +5,7 @@
| cyclic.go:9:2:9:2 | f | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types.u | f |
| cyclic.go:13:2:13:2 | t | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types.t | t |
| cyclic.go:13:2:13:2 | t | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types.u | t |
| cyclic.go:17:2:17:2 | s | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types.v | s |
| depth.go:6:2:6:2 | b | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types.a | b |
| depth.go:7:2:7:2 | c | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types.a | c |
| depth.go:11:2:11:2 | f | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types.a | f |
@@ -13,6 +14,11 @@
| depth.go:15:2:15:2 | d | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types.c | d |
| depth.go:19:2:19:2 | f | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types.c | f |
| depth.go:19:2:19:2 | f | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types.d | f |
| embedded.go:4:2:4:2 | A | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types.Baz | A |
| embedded.go:4:2:4:2 | A | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types.Qux | A |
| 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 |
| 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 |

View File

@@ -5,6 +5,7 @@
| cyclic.go:9:2:9:2 | f | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types | u | f |
| cyclic.go:13:2:13:2 | t | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types | t | t |
| cyclic.go:13:2:13:2 | t | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types | u | t |
| cyclic.go:17:2:17:2 | s | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types | v | s |
| depth.go:6:2:6:2 | b | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types | a | b |
| depth.go:7:2:7:2 | c | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types | a | c |
| depth.go:11:2:11:2 | f | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types | a | f |
@@ -13,6 +14,11 @@
| depth.go:15:2:15:2 | d | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types | c | d |
| depth.go:19:2:19:2 | f | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types | c | f |
| depth.go:19:2:19:2 | f | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types | d | f |
| embedded.go:4:2:4:2 | A | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types | Baz | A |
| embedded.go:4:2:4:2 | A | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types | Qux | A |
| 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 |
| 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 |

View File

@@ -16,5 +16,6 @@
| embedder | f | func() int |
| embedder2 | f | func() int |
| embedder3 | f | func() int |
| embedder4 | f | func() int |
| ptrembedder | f | func() int |
| ptrembedder | g | func() int |

View File

@@ -5,9 +5,12 @@
| AExtended | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types/pkg1.AExtended |
| B | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types/pkg1.B |
| 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 |
| 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 |
| 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 |
@@ -26,3 +29,4 @@
| 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 |
| 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 |

View File

@@ -1,8 +1,13 @@
| Bar | pkg1/tst.go:29:10:31:1 | struct type | flag | bool |
| Baz | embedded.go:3:10:5:1 | struct type | A | string |
| 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 |
| 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 |
| T | pkg1/tst.go:3:8:7:1 | struct type | Foo | Foo |
| T | pkg1/tst.go:3:8:7:1 | struct type | f | int |
@@ -46,3 +51,4 @@
| u | cyclic.go:12:8:14:1 | struct type | f | int |
| u | cyclic.go:12:8:14:1 | struct type | t | t |
| u | cyclic.go:12:8:14:1 | struct type | u | * u |
| v | cyclic.go:16:8:18:1 | struct type | s | s |

View File

@@ -5,9 +5,12 @@
| AExtended | AExtended |
| B | B |
| Bar | Bar |
| Baz | Baz |
| C | C |
| EmbedsBaz | EmbedsBaz |
| Foo | Foo |
| G | G |
| Qux | Qux |
| T | T |
| T | T |
| T2 | T2 |
@@ -26,3 +29,4 @@
| s | s |
| t | t |
| u | u |
| v | v |

View File

@@ -12,3 +12,12 @@ type t struct {
type u struct {
t
}
type v struct {
s
}
// the below will cause the test to not terminate
// type w struct {
// v
// }

View File

@@ -0,0 +1,15 @@
package main
type Baz struct {
A string
}
type Qux struct {
*Baz
}
// EmbedsBaz should have a field A but does not
type EmbedsBaz struct {
Qux
Baz string
}