mirror of
https://github.com/github/codeql.git
synced 2025-12-16 16:53:25 +01:00
Merge pull request #17618 from owen-mc/go/mad/subtypes-promoted-methods
Go: Make the models-as-data subtypes column do something more sensible for promoted methods
This commit is contained in:
@@ -0,0 +1,4 @@
|
||||
---
|
||||
category: fix
|
||||
---
|
||||
* The behaviour of the `subtypes` column in models-as-data now matches other languages more closely.
|
||||
@@ -25,8 +25,12 @@
|
||||
* packages in the group `<groupname>` according to the `packageGrouping`
|
||||
* predicate.
|
||||
* 2. The `type` column selects a type within that package.
|
||||
* 3. The `subtypes` is a boolean that indicates whether to jump to an
|
||||
* arbitrary subtype of that type.
|
||||
* 3. The `subtypes` column is a boolean that controls what restrictions we
|
||||
* place on the type `t` of the selector base when accessing a field or
|
||||
* calling a method. When it is false, `t` must be the exact type specified
|
||||
* by this row. When it is true, `t` may be a type which embeds the specified
|
||||
* type, and for interface methods `t` may be a type which implements the
|
||||
* interface.
|
||||
* 4. The `name` column optionally selects a specific named member of the type.
|
||||
* 5. The `signature` column is always empty.
|
||||
* 6. The `ext` column is always empty.
|
||||
@@ -470,17 +474,24 @@ SourceSinkInterpretationInput::SourceOrSinkElement interpretElement(
|
||||
elementSpec(pkg, type, subtypes, name, signature, ext) and
|
||||
// Go does not need to distinguish functions with signature
|
||||
signature = "" and
|
||||
(
|
||||
exists(Field f | f.hasQualifiedName(interpretPackage(pkg), type, name) | result.asEntity() = f)
|
||||
exists(string p | p = interpretPackage(pkg) |
|
||||
exists(Entity e | result.hasFullInfo(e, p, type, subtypes) |
|
||||
e.(Field).hasQualifiedName(p, type, name) or
|
||||
e.(Method).hasQualifiedName(p, type, name)
|
||||
)
|
||||
or
|
||||
exists(Method m | m.hasQualifiedName(interpretPackage(pkg), type, name) |
|
||||
result.asEntity() = m
|
||||
or
|
||||
subtypes = true and result.asEntity().(Method).implementsIncludingInterfaceMethods(m)
|
||||
subtypes = true and
|
||||
// p.type is an interface and we include types which implement it
|
||||
exists(Method m2, string pkg2, string type2 |
|
||||
m2.getReceiverType().implements(p, type) and
|
||||
m2.getName() = name and
|
||||
m2.getReceiverBaseType().hasQualifiedName(pkg2, type2)
|
||||
|
|
||||
result.hasFullInfo(m2, pkg2, type2, subtypes)
|
||||
)
|
||||
or
|
||||
type = "" and
|
||||
exists(Entity e | e.hasQualifiedName(interpretPackage(pkg), name) | result.asEntity() = e)
|
||||
exists(Entity e | e.hasQualifiedName(p, name) | result.asOtherEntity() = e)
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -149,21 +149,63 @@ module SourceSinkInterpretationInput implements
|
||||
)
|
||||
}
|
||||
|
||||
// Note that due to embedding, which is currently implemented via some
|
||||
// Methods having multiple qualified names, a given Method is liable to have
|
||||
// more than one SourceOrSinkElement, one for each of the names it claims.
|
||||
private newtype TSourceOrSinkElement =
|
||||
TEntityElement(Entity e) or
|
||||
TMethodEntityElement(Method m, string pkg, string type, boolean subtypes) {
|
||||
m.hasQualifiedName(pkg, type, _) and
|
||||
subtypes = [true, false]
|
||||
} or
|
||||
TFieldEntityElement(Field f, string pkg, string type, boolean subtypes) {
|
||||
f.hasQualifiedName(pkg, type, _) and
|
||||
subtypes = [true, false]
|
||||
} or
|
||||
TOtherEntityElement(Entity e) {
|
||||
not e instanceof Method and
|
||||
not e instanceof Field
|
||||
} or
|
||||
TAstElement(AstNode n)
|
||||
|
||||
/** An element representable by CSV modeling. */
|
||||
class SourceOrSinkElement extends TSourceOrSinkElement {
|
||||
/** Gets this source or sink element as an entity, if it is one. */
|
||||
Entity asEntity() { this = TEntityElement(result) }
|
||||
Entity asEntity() {
|
||||
result = [this.asMethodEntity(), this.asFieldEntity(), this.asOtherEntity()]
|
||||
}
|
||||
|
||||
/** Gets this source or sink element as a method, if it is one. */
|
||||
Method asMethodEntity() { this = TMethodEntityElement(result, _, _, _) }
|
||||
|
||||
/** Gets this source or sink element as a field, if it is one. */
|
||||
Field asFieldEntity() { this = TFieldEntityElement(result, _, _, _) }
|
||||
|
||||
/** Gets this source or sink element as an entity which isn't a field or method, if it is one. */
|
||||
Entity asOtherEntity() { this = TOtherEntityElement(result) }
|
||||
|
||||
/** Gets this source or sink element as an AST node, if it is one. */
|
||||
AstNode asAstNode() { this = TAstElement(result) }
|
||||
|
||||
/**
|
||||
* Holds if this source or sink element is a method or field that was specified
|
||||
* with the given values for `e`, `pkg`, `type` and `subtypes`.
|
||||
*/
|
||||
predicate hasFullInfo(Entity e, string pkg, string type, boolean subtypes) {
|
||||
this = TMethodEntityElement(e, pkg, type, subtypes) or
|
||||
this = TFieldEntityElement(e, pkg, type, subtypes)
|
||||
}
|
||||
|
||||
/** Gets a textual representation of this source or sink element. */
|
||||
string toString() {
|
||||
(this instanceof TOtherEntityElement or this instanceof TAstElement) and
|
||||
result = "element representing " + [this.asEntity().toString(), this.asAstNode().toString()]
|
||||
or
|
||||
exists(Entity e, string pkg, string name, boolean subtypes |
|
||||
this.hasFullInfo(e, pkg, name, subtypes) and
|
||||
result =
|
||||
"element representing " + e.toString() + " with receiver type " + pkg + "." + name +
|
||||
" and subtypes=" + subtypes
|
||||
)
|
||||
}
|
||||
|
||||
/** Gets the location of this element. */
|
||||
@@ -203,7 +245,17 @@ module SourceSinkInterpretationInput implements
|
||||
|
||||
/** Gets the target of this call, if any. */
|
||||
SourceOrSinkElement getCallTarget() {
|
||||
result.asEntity() = this.asCall().getNode().(DataFlow::CallNode).getTarget()
|
||||
exists(DataFlow::CallNode cn, Function callTarget |
|
||||
cn = this.asCall().getNode() and
|
||||
callTarget = cn.getTarget()
|
||||
|
|
||||
(
|
||||
result.asOtherEntity() = callTarget
|
||||
or
|
||||
callTarget instanceof Method and
|
||||
result = getElementWithQualifier(callTarget, cn.getReceiver())
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
/** Gets a textual representation of this node. */
|
||||
@@ -228,6 +280,105 @@ module SourceSinkInterpretationInput implements
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a method or field spec for `e` which applies in the context of
|
||||
* qualifier `qual`.
|
||||
*
|
||||
* Note that naively checking `e`'s qualified name is not correct, because
|
||||
* `Method`s and `Field`s may have multiple qualified names due to embedding.
|
||||
* We must instead check that the package and type name given by
|
||||
* `result.hasFullInfo` refer to either `qual`'s type or to a type it embeds.
|
||||
*/
|
||||
bindingset[e, qual]
|
||||
pragma[inline_late]
|
||||
private SourceOrSinkElement getElementWithQualifier(Entity e, DataFlow::Node qual) {
|
||||
exists(boolean subtypes, Type syntacticQualBaseType, Type targetType |
|
||||
syntacticQualBaseType = getSyntacticQualifierBaseType(qual) and
|
||||
result = constructElement(e, targetType, subtypes)
|
||||
|
|
||||
subtypes = [true, false] and
|
||||
syntacticQualBaseType = targetType
|
||||
or
|
||||
subtypes = true and
|
||||
(
|
||||
// `syntacticQualBaseType`'s underlying type might be an interface type and `sse`
|
||||
// might refer to a method defined on an interface embedded within it.
|
||||
targetType =
|
||||
syntacticQualBaseType.getUnderlyingType().(InterfaceType).getAnEmbeddedInterface()
|
||||
or
|
||||
// `syntacticQualBaseType`'s underlying type might be a struct type and `sse`
|
||||
// might be a promoted method or field in it.
|
||||
targetType = getAnIntermediateEmbeddedType(e, syntacticQualBaseType.getUnderlyingType())
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
bindingset[e, targetType, subtypes]
|
||||
pragma[inline_late]
|
||||
private SourceOrSinkElement constructElement(Entity e, Type targetType, boolean subtypes) {
|
||||
exists(string pkg, string typename |
|
||||
targetType.hasQualifiedName(pkg, typename) and
|
||||
result.hasFullInfo(e, pkg, typename, subtypes)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the type of an embedded field of `st` which is on the path to `e`,
|
||||
* which is a promoted method or field of `st`, or its base type if it's a
|
||||
* pointer type.
|
||||
*/
|
||||
private Type getAnIntermediateEmbeddedType(Entity e, StructType st) {
|
||||
exists(Field field1, Field field2, int depth1, int depth2, Type t2 |
|
||||
field1 = st.getFieldAtDepth(_, depth1) and
|
||||
field2 = st.getFieldAtDepth(_, depth2) and
|
||||
result = lookThroughPointerType(field1.getType()) and
|
||||
t2 = lookThroughPointerType(field2.getType()) and
|
||||
(
|
||||
field1 = field2
|
||||
or
|
||||
field2 = result.getUnderlyingType().(StructType).getFieldAtDepth(_, depth2 - depth1 - 1)
|
||||
)
|
||||
|
|
||||
e.(Method).getReceiverBaseType() = t2
|
||||
or
|
||||
e.(Field).getDeclaringType() = t2.getUnderlyingType()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the base type of `underlying`, where `n` is of the form
|
||||
* `implicitDeref?(underlying.implicitFieldRead1.implicitFieldRead2...)`
|
||||
*
|
||||
* For Go syntax like `qualifier.method()` or `qualifier.field`, this is the type of `qualifier`, before any
|
||||
* implicit dereference is interposed because `qualifier` is of pointer type, or implicit field accesses
|
||||
* navigate to any embedded struct types that truly host `field`.
|
||||
*/
|
||||
private Type getSyntacticQualifierBaseType(DataFlow::Node n) {
|
||||
exists(DataFlow::Node n2 |
|
||||
// look through implicit dereference, if there is one
|
||||
not exists(n.asInstruction().(IR::EvalImplicitDerefInstruction).getOperand()) and
|
||||
n2 = n
|
||||
or
|
||||
n2.asExpr() = n.asInstruction().(IR::EvalImplicitDerefInstruction).getOperand()
|
||||
|
|
||||
result = lookThroughPointerType(skipImplicitFieldReads(n2).getType())
|
||||
)
|
||||
}
|
||||
|
||||
private DataFlow::Node skipImplicitFieldReads(DataFlow::Node n) {
|
||||
not exists(lookThroughImplicitFieldRead(n)) and result = n
|
||||
or
|
||||
result = skipImplicitFieldReads(lookThroughImplicitFieldRead(n))
|
||||
}
|
||||
|
||||
private DataFlow::Node lookThroughImplicitFieldRead(DataFlow::Node n) {
|
||||
result.asInstruction() =
|
||||
n.(DataFlow::InstructionNode)
|
||||
.asInstruction()
|
||||
.(IR::ImplicitFieldReadInstruction)
|
||||
.getBaseInstruction()
|
||||
}
|
||||
|
||||
/** Provides additional sink specification logic. */
|
||||
bindingset[c]
|
||||
predicate interpretOutput(string c, InterpretNode mid, InterpretNode node) {
|
||||
@@ -242,10 +393,12 @@ module SourceSinkInterpretationInput implements
|
||||
e = mid.asElement()
|
||||
|
|
||||
(c = "Parameter" or c = "") and
|
||||
node.asNode().asParameter() = e.asEntity()
|
||||
n.asParameter() = pragma[only_bind_into](e).asEntity()
|
||||
or
|
||||
c = "" and
|
||||
n.(DataFlow::FieldReadNode).getField() = e.asEntity()
|
||||
exists(DataFlow::FieldReadNode frn | frn = n |
|
||||
c = "" and
|
||||
pragma[only_bind_into](e) = getElementWithQualifier(frn.getField(), frn.getBase())
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -259,10 +412,13 @@ module SourceSinkInterpretationInput implements
|
||||
mid.asCallable() = getNodeEnclosingCallable(ret)
|
||||
)
|
||||
or
|
||||
exists(DataFlow::Write fw, Field f |
|
||||
exists(SourceOrSinkElement e, DataFlow::Write fw, DataFlow::Node base, Field f |
|
||||
e = mid.asElement() and
|
||||
f = e.asFieldEntity()
|
||||
|
|
||||
c = "" and
|
||||
f = mid.asElement().asEntity() and
|
||||
fw.writesField(_, f, node.asNode())
|
||||
fw.writesField(base, f, node.asNode()) and
|
||||
pragma[only_bind_into](e) = getElementWithQualifier(f, base)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@ extensions:
|
||||
pack: codeql/go-all
|
||||
extensible: sourceModel
|
||||
data:
|
||||
- ["github.com/nonexistent/test", "I1", False, "Source", "", "", "ReturnValue", "remote", "manual"]
|
||||
- ["github.com/nonexistent/test", "I1", False, "Source", "", "", "ReturnValue", "qltest", "manual"]
|
||||
- addsTo:
|
||||
pack: codeql/go-all
|
||||
extensible: summaryModel
|
||||
@@ -13,4 +13,4 @@ extensions:
|
||||
pack: codeql/go-all
|
||||
extensible: sinkModel
|
||||
data:
|
||||
- ["github.com/nonexistent/test", "I1", False, "Sink", "", "", "Argument[0]", "path-injection", "manual"]
|
||||
- ["github.com/nonexistent/test", "I1", False, "Sink", "", "", "Argument[0]", "qltest", "manual"]
|
||||
@@ -1,14 +1,12 @@
|
||||
import go
|
||||
import semmle.go.dataflow.ExternalFlow
|
||||
import ModelValidation
|
||||
import semmle.go.dataflow.internal.FlowSummaryImpl as FlowSummaryImpl
|
||||
import TestUtilities.InlineExpectationsTest
|
||||
import MakeTest<FlowTest>
|
||||
|
||||
module Config implements DataFlow::ConfigSig {
|
||||
predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource }
|
||||
predicate isSource(DataFlow::Node source) { sourceNode(source, "qltest") }
|
||||
|
||||
predicate isSink(DataFlow::Node sink) { sink = any(FileSystemAccess fsa).getAPathArgument() }
|
||||
predicate isSink(DataFlow::Node sink) { sinkNode(sink, "qltest") }
|
||||
}
|
||||
|
||||
module Flow = TaintTracking::Global<Config>;
|
||||
@@ -3,7 +3,7 @@ extensions:
|
||||
pack: codeql/go-all
|
||||
extensible: sourceModel
|
||||
data:
|
||||
- ["github.com/nonexistent/test", "I1", True, "Source", "", "", "ReturnValue", "remote", "manual"]
|
||||
- ["github.com/nonexistent/test", "I1", True, "Source", "", "", "ReturnValue", "qltest", "manual"]
|
||||
- addsTo:
|
||||
pack: codeql/go-all
|
||||
extensible: summaryModel
|
||||
@@ -13,4 +13,4 @@ extensions:
|
||||
pack: codeql/go-all
|
||||
extensible: sinkModel
|
||||
data:
|
||||
- ["github.com/nonexistent/test", "I1", True, "Sink", "", "", "Argument[0]", "path-injection", "manual"]
|
||||
- ["github.com/nonexistent/test", "I1", True, "Sink", "", "", "Argument[0]", "qltest", "manual"]
|
||||
@@ -1,14 +1,12 @@
|
||||
import go
|
||||
import semmle.go.dataflow.ExternalFlow
|
||||
import ModelValidation
|
||||
import semmle.go.dataflow.internal.FlowSummaryImpl as FlowSummaryImpl
|
||||
import TestUtilities.InlineExpectationsTest
|
||||
import MakeTest<FlowTest>
|
||||
|
||||
module Config implements DataFlow::ConfigSig {
|
||||
predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource }
|
||||
predicate isSource(DataFlow::Node source) { sourceNode(source, "qltest") }
|
||||
|
||||
predicate isSink(DataFlow::Node sink) { sink = any(FileSystemAccess fsa).getAPathArgument() }
|
||||
predicate isSink(DataFlow::Node sink) { sinkNode(sink, "qltest") }
|
||||
}
|
||||
|
||||
module Flow = TaintTracking::Global<Config>;
|
||||
@@ -3,7 +3,7 @@ extensions:
|
||||
pack: codeql/go-all
|
||||
extensible: sourceModel
|
||||
data:
|
||||
- ["github.com/nonexistent/test", "I2", False, "Source", "", "", "ReturnValue", "remote", "manual"]
|
||||
- ["github.com/nonexistent/test", "I2", False, "Source", "", "", "ReturnValue", "qltest", "manual"]
|
||||
- addsTo:
|
||||
pack: codeql/go-all
|
||||
extensible: summaryModel
|
||||
@@ -13,4 +13,4 @@ extensions:
|
||||
pack: codeql/go-all
|
||||
extensible: sinkModel
|
||||
data:
|
||||
- ["github.com/nonexistent/test", "I2", False, "Sink", "", "", "Argument[0]", "path-injection", "manual"]
|
||||
- ["github.com/nonexistent/test", "I2", False, "Sink", "", "", "Argument[0]", "qltest", "manual"]
|
||||
@@ -1,14 +1,12 @@
|
||||
import go
|
||||
import semmle.go.dataflow.ExternalFlow
|
||||
import ModelValidation
|
||||
import semmle.go.dataflow.internal.FlowSummaryImpl as FlowSummaryImpl
|
||||
import TestUtilities.InlineExpectationsTest
|
||||
import MakeTest<FlowTest>
|
||||
|
||||
module Config implements DataFlow::ConfigSig {
|
||||
predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource }
|
||||
predicate isSource(DataFlow::Node source) { sourceNode(source, "qltest") }
|
||||
|
||||
predicate isSink(DataFlow::Node sink) { sink = any(FileSystemAccess fsa).getAPathArgument() }
|
||||
predicate isSink(DataFlow::Node sink) { sinkNode(sink, "qltest") }
|
||||
}
|
||||
|
||||
module Flow = TaintTracking::Global<Config>;
|
||||
@@ -3,7 +3,7 @@ extensions:
|
||||
pack: codeql/go-all
|
||||
extensible: sourceModel
|
||||
data:
|
||||
- ["github.com/nonexistent/test", "I2", True, "Source", "", "", "ReturnValue", "remote", "manual"]
|
||||
- ["github.com/nonexistent/test", "I2", True, "Source", "", "", "ReturnValue", "qltest", "manual"]
|
||||
- addsTo:
|
||||
pack: codeql/go-all
|
||||
extensible: summaryModel
|
||||
@@ -13,4 +13,4 @@ extensions:
|
||||
pack: codeql/go-all
|
||||
extensible: sinkModel
|
||||
data:
|
||||
- ["github.com/nonexistent/test", "I2", True, "Sink", "", "", "Argument[0]", "path-injection", "manual"]
|
||||
- ["github.com/nonexistent/test", "I2", True, "Sink", "", "", "Argument[0]", "qltest", "manual"]
|
||||
@@ -1,14 +1,12 @@
|
||||
import go
|
||||
import semmle.go.dataflow.ExternalFlow
|
||||
import ModelValidation
|
||||
import semmle.go.dataflow.internal.FlowSummaryImpl as FlowSummaryImpl
|
||||
import TestUtilities.InlineExpectationsTest
|
||||
import MakeTest<FlowTest>
|
||||
|
||||
module Config implements DataFlow::ConfigSig {
|
||||
predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource }
|
||||
predicate isSource(DataFlow::Node source) { sourceNode(source, "qltest") }
|
||||
|
||||
predicate isSink(DataFlow::Node sink) { sink = any(FileSystemAccess fsa).getAPathArgument() }
|
||||
predicate isSink(DataFlow::Node sink) { sinkNode(sink, "qltest") }
|
||||
}
|
||||
|
||||
module Flow = TaintTracking::Global<Config>;
|
||||
@@ -3,7 +3,7 @@ extensions:
|
||||
pack: codeql/go-all
|
||||
extensible: sourceModel
|
||||
data:
|
||||
- ["github.com/nonexistent/test", "IEmbedI1", True, "Source", "", "", "ReturnValue", "remote", "manual"]
|
||||
- ["github.com/nonexistent/test", "IEmbedI1", True, "Source", "", "", "ReturnValue", "qltest", "manual"]
|
||||
- addsTo:
|
||||
pack: codeql/go-all
|
||||
extensible: summaryModel
|
||||
@@ -13,4 +13,4 @@ extensions:
|
||||
pack: codeql/go-all
|
||||
extensible: sinkModel
|
||||
data:
|
||||
- ["github.com/nonexistent/test", "IEmbedI1", True, "Sink", "", "", "Argument[0]", "path-injection", "manual"]
|
||||
- ["github.com/nonexistent/test", "IEmbedI1", True, "Sink", "", "", "Argument[0]", "qltest", "manual"]
|
||||
|
||||
@@ -1,14 +1,12 @@
|
||||
import go
|
||||
import semmle.go.dataflow.ExternalFlow
|
||||
import ModelValidation
|
||||
import semmle.go.dataflow.internal.FlowSummaryImpl as FlowSummaryImpl
|
||||
import TestUtilities.InlineExpectationsTest
|
||||
import MakeTest<FlowTest>
|
||||
|
||||
module Config implements DataFlow::ConfigSig {
|
||||
predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource }
|
||||
predicate isSource(DataFlow::Node source) { sourceNode(source, "qltest") }
|
||||
|
||||
predicate isSink(DataFlow::Node sink) { sink = any(FileSystemAccess fsa).getAPathArgument() }
|
||||
predicate isSink(DataFlow::Node sink) { sinkNode(sink, "qltest") }
|
||||
}
|
||||
|
||||
module Flow = TaintTracking::Global<Config>;
|
||||
|
||||
@@ -3,7 +3,7 @@ extensions:
|
||||
pack: codeql/go-all
|
||||
extensible: sourceModel
|
||||
data:
|
||||
- ["github.com/nonexistent/test", "IEmbedI2", True, "Source", "", "", "ReturnValue", "remote", "manual"]
|
||||
- ["github.com/nonexistent/test", "IEmbedI2", True, "Source", "", "", "ReturnValue", "qltest", "manual"]
|
||||
- addsTo:
|
||||
pack: codeql/go-all
|
||||
extensible: summaryModel
|
||||
@@ -13,4 +13,4 @@ extensions:
|
||||
pack: codeql/go-all
|
||||
extensible: sinkModel
|
||||
data:
|
||||
- ["github.com/nonexistent/test", "IEmbedI2", True, "Sink", "", "", "Argument[0]", "path-injection", "manual"]
|
||||
- ["github.com/nonexistent/test", "IEmbedI2", True, "Sink", "", "", "Argument[0]", "qltest", "manual"]
|
||||
|
||||
@@ -1,14 +1,12 @@
|
||||
import go
|
||||
import semmle.go.dataflow.ExternalFlow
|
||||
import ModelValidation
|
||||
import semmle.go.dataflow.internal.FlowSummaryImpl as FlowSummaryImpl
|
||||
import TestUtilities.InlineExpectationsTest
|
||||
import MakeTest<FlowTest>
|
||||
|
||||
module Config implements DataFlow::ConfigSig {
|
||||
predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource }
|
||||
predicate isSource(DataFlow::Node source) { sourceNode(source, "qltest") }
|
||||
|
||||
predicate isSink(DataFlow::Node sink) { sink = any(FileSystemAccess fsa).getAPathArgument() }
|
||||
predicate isSink(DataFlow::Node sink) { sinkNode(sink, "qltest") }
|
||||
}
|
||||
|
||||
module Flow = TaintTracking::Global<Config>;
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
extensions:
|
||||
- addsTo:
|
||||
pack: codeql/go-all
|
||||
extensible: sourceModel
|
||||
data:
|
||||
- ["github.com/nonexistent/test", "PImplEmbedI1", True, "Source", "", "", "ReturnValue", "qltest", "manual"]
|
||||
- ["github.com/nonexistent/test", "PImplEmbedI1", True, "SourceField", "", "", "", "qltest", "manual"]
|
||||
- addsTo:
|
||||
pack: codeql/go-all
|
||||
extensible: summaryModel
|
||||
data:
|
||||
- ["github.com/nonexistent/test", "PImplEmbedI1", True, "Step", "", "", "Argument[0]", "ReturnValue", "value", "manual"]
|
||||
- addsTo:
|
||||
pack: codeql/go-all
|
||||
extensible: sinkModel
|
||||
data:
|
||||
- ["github.com/nonexistent/test", "PImplEmbedI1", True, "Sink", "", "", "Argument[0]", "qltest", "manual"]
|
||||
- ["github.com/nonexistent/test", "PImplEmbedI1", True, "SinkField", "", "", "", "qltest", "manual"]
|
||||
@@ -0,0 +1,26 @@
|
||||
import go
|
||||
import ModelValidation
|
||||
import TestUtilities.InlineExpectationsTest
|
||||
import MakeTest<FlowTest>
|
||||
|
||||
module Config implements DataFlow::ConfigSig {
|
||||
predicate isSource(DataFlow::Node source) { sourceNode(source, "qltest") }
|
||||
|
||||
predicate isSink(DataFlow::Node sink) { sinkNode(sink, "qltest") }
|
||||
}
|
||||
|
||||
module Flow = TaintTracking::Global<Config>;
|
||||
|
||||
module FlowTest implements TestSig {
|
||||
string getARelevantTag() { result = "PImplEmbedI1[t]" }
|
||||
|
||||
predicate hasActualResult(Location location, string element, string tag, string value) {
|
||||
tag = "PImplEmbedI1[t]" and
|
||||
exists(DataFlow::Node sink | Flow::flowTo(sink) |
|
||||
sink.hasLocationInfo(location.getFile().getAbsolutePath(), location.getStartLine(),
|
||||
location.getStartColumn(), location.getEndLine(), location.getEndColumn()) and
|
||||
element = sink.toString() and
|
||||
value = ""
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
extensions:
|
||||
- addsTo:
|
||||
pack: codeql/go-all
|
||||
extensible: sourceModel
|
||||
data:
|
||||
- ["github.com/nonexistent/test", "PImplEmbedI2", True, "Source", "", "", "ReturnValue", "qltest", "manual"]
|
||||
- addsTo:
|
||||
pack: codeql/go-all
|
||||
extensible: summaryModel
|
||||
data:
|
||||
- ["github.com/nonexistent/test", "PImplEmbedI2", True, "Step", "", "", "Argument[0]", "ReturnValue", "value", "manual"]
|
||||
- addsTo:
|
||||
pack: codeql/go-all
|
||||
extensible: sinkModel
|
||||
data:
|
||||
- ["github.com/nonexistent/test", "PImplEmbedI2", True, "Sink", "", "", "Argument[0]", "qltest", "manual"]
|
||||
@@ -0,0 +1,26 @@
|
||||
import go
|
||||
import ModelValidation
|
||||
import TestUtilities.InlineExpectationsTest
|
||||
import MakeTest<FlowTest>
|
||||
|
||||
module Config implements DataFlow::ConfigSig {
|
||||
predicate isSource(DataFlow::Node source) { sourceNode(source, "qltest") }
|
||||
|
||||
predicate isSink(DataFlow::Node sink) { sinkNode(sink, "qltest") }
|
||||
}
|
||||
|
||||
module Flow = TaintTracking::Global<Config>;
|
||||
|
||||
module FlowTest implements TestSig {
|
||||
string getARelevantTag() { result = "PImplEmbedI2[t]" }
|
||||
|
||||
predicate hasActualResult(Location location, string element, string tag, string value) {
|
||||
tag = "PImplEmbedI2[t]" and
|
||||
exists(DataFlow::Node sink | Flow::flowTo(sink) |
|
||||
sink.hasLocationInfo(location.getFile().getAbsolutePath(), location.getStartLine(),
|
||||
location.getStartColumn(), location.getEndLine(), location.getEndColumn()) and
|
||||
element = sink.toString() and
|
||||
value = ""
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
testFailures
|
||||
invalidModelRow
|
||||
failures
|
||||
@@ -3,7 +3,8 @@ extensions:
|
||||
pack: codeql/go-all
|
||||
extensible: sourceModel
|
||||
data:
|
||||
- ["github.com/nonexistent/test", "S1", False, "Source", "", "", "ReturnValue", "remote", "manual"]
|
||||
- ["github.com/nonexistent/test", "S1", False, "Source", "", "", "ReturnValue", "qltest", "manual"]
|
||||
- ["github.com/nonexistent/test", "S1", False, "SourceField", "", "", "", "qltest", "manual"]
|
||||
- addsTo:
|
||||
pack: codeql/go-all
|
||||
extensible: summaryModel
|
||||
@@ -13,4 +14,5 @@ extensions:
|
||||
pack: codeql/go-all
|
||||
extensible: sinkModel
|
||||
data:
|
||||
- ["github.com/nonexistent/test", "S1", False, "Sink", "", "", "Argument[0]", "path-injection", "manual"]
|
||||
- ["github.com/nonexistent/test", "S1", False, "Sink", "", "", "Argument[0]", "qltest", "manual"]
|
||||
- ["github.com/nonexistent/test", "S1", False, "SinkField", "", "", "", "qltest", "manual"]
|
||||
@@ -1,14 +1,12 @@
|
||||
import go
|
||||
import semmle.go.dataflow.ExternalFlow
|
||||
import ModelValidation
|
||||
import semmle.go.dataflow.internal.FlowSummaryImpl as FlowSummaryImpl
|
||||
import TestUtilities.InlineExpectationsTest
|
||||
import MakeTest<FlowTest>
|
||||
|
||||
module Config implements DataFlow::ConfigSig {
|
||||
predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource }
|
||||
predicate isSource(DataFlow::Node source) { sourceNode(source, "qltest") }
|
||||
|
||||
predicate isSink(DataFlow::Node sink) { sink = any(FileSystemAccess fsa).getAPathArgument() }
|
||||
predicate isSink(DataFlow::Node sink) { sinkNode(sink, "qltest") }
|
||||
}
|
||||
|
||||
module Flow = TaintTracking::Global<Config>;
|
||||
@@ -0,0 +1,3 @@
|
||||
testFailures
|
||||
invalidModelRow
|
||||
failures
|
||||
@@ -3,7 +3,8 @@ extensions:
|
||||
pack: codeql/go-all
|
||||
extensible: sourceModel
|
||||
data:
|
||||
- ["github.com/nonexistent/test", "S1", True, "Source", "", "", "ReturnValue", "remote", "manual"]
|
||||
- ["github.com/nonexistent/test", "S1", True, "Source", "", "", "ReturnValue", "qltest", "manual"]
|
||||
- ["github.com/nonexistent/test", "S1", True, "SourceField", "", "", "", "qltest", "manual"]
|
||||
- addsTo:
|
||||
pack: codeql/go-all
|
||||
extensible: summaryModel
|
||||
@@ -13,4 +14,5 @@ extensions:
|
||||
pack: codeql/go-all
|
||||
extensible: sinkModel
|
||||
data:
|
||||
- ["github.com/nonexistent/test", "S1", True, "Sink", "", "", "Argument[0]", "path-injection", "manual"]
|
||||
- ["github.com/nonexistent/test", "S1", True, "Sink", "", "", "Argument[0]", "qltest", "manual"]
|
||||
- ["github.com/nonexistent/test", "S1", True, "SinkField", "", "", "", "qltest", "manual"]
|
||||
@@ -1,14 +1,12 @@
|
||||
import go
|
||||
import semmle.go.dataflow.ExternalFlow
|
||||
import ModelValidation
|
||||
import semmle.go.dataflow.internal.FlowSummaryImpl as FlowSummaryImpl
|
||||
import TestUtilities.InlineExpectationsTest
|
||||
import MakeTest<FlowTest>
|
||||
|
||||
module Config implements DataFlow::ConfigSig {
|
||||
predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource }
|
||||
predicate isSource(DataFlow::Node source) { sourceNode(source, "qltest") }
|
||||
|
||||
predicate isSink(DataFlow::Node sink) { sink = any(FileSystemAccess fsa).getAPathArgument() }
|
||||
predicate isSink(DataFlow::Node sink) { sinkNode(sink, "qltest") }
|
||||
}
|
||||
|
||||
module Flow = TaintTracking::Global<Config>;
|
||||
@@ -3,7 +3,8 @@ extensions:
|
||||
pack: codeql/go-all
|
||||
extensible: sourceModel
|
||||
data:
|
||||
- ["github.com/nonexistent/test", "SEmbedI1", True, "Source", "", "", "ReturnValue", "remote", "manual"]
|
||||
- ["github.com/nonexistent/test", "SEmbedI1", True, "Source", "", "", "ReturnValue", "qltest", "manual"]
|
||||
- ["github.com/nonexistent/test", "SEmbedI1", True, "SourceField", "", "", "", "qltest", "manual"]
|
||||
- addsTo:
|
||||
pack: codeql/go-all
|
||||
extensible: summaryModel
|
||||
@@ -13,4 +14,5 @@ extensions:
|
||||
pack: codeql/go-all
|
||||
extensible: sinkModel
|
||||
data:
|
||||
- ["github.com/nonexistent/test", "SEmbedI1", True, "Sink", "", "", "Argument[0]", "path-injection", "manual"]
|
||||
- ["github.com/nonexistent/test", "SEmbedI1", True, "Sink", "", "", "Argument[0]", "qltest", "manual"]
|
||||
- ["github.com/nonexistent/test", "SEmbedI1", True, "SinkField", "", "", "", "qltest", "manual"]
|
||||
|
||||
@@ -1,14 +1,12 @@
|
||||
import go
|
||||
import semmle.go.dataflow.ExternalFlow
|
||||
import ModelValidation
|
||||
import semmle.go.dataflow.internal.FlowSummaryImpl as FlowSummaryImpl
|
||||
import TestUtilities.InlineExpectationsTest
|
||||
import MakeTest<FlowTest>
|
||||
|
||||
module Config implements DataFlow::ConfigSig {
|
||||
predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource }
|
||||
predicate isSource(DataFlow::Node source) { sourceNode(source, "qltest") }
|
||||
|
||||
predicate isSink(DataFlow::Node sink) { sink = any(FileSystemAccess fsa).getAPathArgument() }
|
||||
predicate isSink(DataFlow::Node sink) { sinkNode(sink, "qltest") }
|
||||
}
|
||||
|
||||
module Flow = TaintTracking::Global<Config>;
|
||||
|
||||
@@ -3,7 +3,7 @@ extensions:
|
||||
pack: codeql/go-all
|
||||
extensible: sourceModel
|
||||
data:
|
||||
- ["github.com/nonexistent/test", "SEmbedI2", True, "Source", "", "", "ReturnValue", "remote", "manual"]
|
||||
- ["github.com/nonexistent/test", "SEmbedI2", True, "Source", "", "", "ReturnValue", "qltest", "manual"]
|
||||
- addsTo:
|
||||
pack: codeql/go-all
|
||||
extensible: summaryModel
|
||||
@@ -13,4 +13,4 @@ extensions:
|
||||
pack: codeql/go-all
|
||||
extensible: sinkModel
|
||||
data:
|
||||
- ["github.com/nonexistent/test", "SEmbedI2", True, "Sink", "", "", "Argument[0]", "path-injection", "manual"]
|
||||
- ["github.com/nonexistent/test", "SEmbedI2", True, "Sink", "", "", "Argument[0]", "qltest", "manual"]
|
||||
|
||||
@@ -1,14 +1,12 @@
|
||||
import go
|
||||
import semmle.go.dataflow.ExternalFlow
|
||||
import ModelValidation
|
||||
import semmle.go.dataflow.internal.FlowSummaryImpl as FlowSummaryImpl
|
||||
import TestUtilities.InlineExpectationsTest
|
||||
import MakeTest<FlowTest>
|
||||
|
||||
module Config implements DataFlow::ConfigSig {
|
||||
predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource }
|
||||
predicate isSource(DataFlow::Node source) { sourceNode(source, "qltest") }
|
||||
|
||||
predicate isSink(DataFlow::Node sink) { sink = any(FileSystemAccess fsa).getAPathArgument() }
|
||||
predicate isSink(DataFlow::Node sink) { sinkNode(sink, "qltest") }
|
||||
}
|
||||
|
||||
module Flow = TaintTracking::Global<Config>;
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
testFailures
|
||||
invalidModelRow
|
||||
failures
|
||||
@@ -0,0 +1,18 @@
|
||||
extensions:
|
||||
- addsTo:
|
||||
pack: codeql/go-all
|
||||
extensible: sourceModel
|
||||
data:
|
||||
- ["github.com/nonexistent/test", "SEmbedP1", True, "Source", "", "", "ReturnValue", "qltest", "manual"]
|
||||
- ["github.com/nonexistent/test", "SEmbedP1", True, "SourceField", "", "", "", "qltest", "manual"]
|
||||
- addsTo:
|
||||
pack: codeql/go-all
|
||||
extensible: summaryModel
|
||||
data:
|
||||
- ["github.com/nonexistent/test", "SEmbedP1", True, "Step", "", "", "Argument[0]", "ReturnValue", "value", "manual"]
|
||||
- addsTo:
|
||||
pack: codeql/go-all
|
||||
extensible: sinkModel
|
||||
data:
|
||||
- ["github.com/nonexistent/test", "SEmbedP1", True, "Sink", "", "", "Argument[0]", "qltest", "manual"]
|
||||
- ["github.com/nonexistent/test", "SEmbedP1", True, "SinkField", "", "", "", "qltest", "manual"]
|
||||
@@ -0,0 +1,26 @@
|
||||
import go
|
||||
import ModelValidation
|
||||
import TestUtilities.InlineExpectationsTest
|
||||
import MakeTest<FlowTest>
|
||||
|
||||
module Config implements DataFlow::ConfigSig {
|
||||
predicate isSource(DataFlow::Node source) { sourceNode(source, "qltest") }
|
||||
|
||||
predicate isSink(DataFlow::Node sink) { sinkNode(sink, "qltest") }
|
||||
}
|
||||
|
||||
module Flow = TaintTracking::Global<Config>;
|
||||
|
||||
module FlowTest implements TestSig {
|
||||
string getARelevantTag() { result = "SEmbedP1[t]" }
|
||||
|
||||
predicate hasActualResult(Location location, string element, string tag, string value) {
|
||||
tag = "SEmbedP1[t]" and
|
||||
exists(DataFlow::Node sink | Flow::flowTo(sink) |
|
||||
sink.hasLocationInfo(location.getFile().getAbsolutePath(), location.getStartLine(),
|
||||
location.getStartColumn(), location.getEndLine(), location.getEndColumn()) and
|
||||
element = sink.toString() and
|
||||
value = ""
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
testFailures
|
||||
invalidModelRow
|
||||
failures
|
||||
@@ -0,0 +1,16 @@
|
||||
extensions:
|
||||
- addsTo:
|
||||
pack: codeql/go-all
|
||||
extensible: sourceModel
|
||||
data:
|
||||
- ["github.com/nonexistent/test", "SEmbedP2", True, "Source", "", "", "ReturnValue", "qltest", "manual"]
|
||||
- addsTo:
|
||||
pack: codeql/go-all
|
||||
extensible: summaryModel
|
||||
data:
|
||||
- ["github.com/nonexistent/test", "SEmbedP2", True, "Step", "", "", "Argument[0]", "ReturnValue", "value", "manual"]
|
||||
- addsTo:
|
||||
pack: codeql/go-all
|
||||
extensible: sinkModel
|
||||
data:
|
||||
- ["github.com/nonexistent/test", "SEmbedP2", True, "Sink", "", "", "Argument[0]", "qltest", "manual"]
|
||||
@@ -0,0 +1,26 @@
|
||||
import go
|
||||
import ModelValidation
|
||||
import TestUtilities.InlineExpectationsTest
|
||||
import MakeTest<FlowTest>
|
||||
|
||||
module Config implements DataFlow::ConfigSig {
|
||||
predicate isSource(DataFlow::Node source) { sourceNode(source, "qltest") }
|
||||
|
||||
predicate isSink(DataFlow::Node sink) { sinkNode(sink, "qltest") }
|
||||
}
|
||||
|
||||
module Flow = TaintTracking::Global<Config>;
|
||||
|
||||
module FlowTest implements TestSig {
|
||||
string getARelevantTag() { result = "SEmbedP2[t]" }
|
||||
|
||||
predicate hasActualResult(Location location, string element, string tag, string value) {
|
||||
tag = "SEmbedP2[t]" and
|
||||
exists(DataFlow::Node sink | Flow::flowTo(sink) |
|
||||
sink.hasLocationInfo(location.getFile().getAbsolutePath(), location.getStartLine(),
|
||||
location.getStartColumn(), location.getEndLine(), location.getEndColumn()) and
|
||||
element = sink.toString() and
|
||||
value = ""
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
testFailures
|
||||
invalidModelRow
|
||||
failures
|
||||
@@ -0,0 +1,18 @@
|
||||
extensions:
|
||||
- addsTo:
|
||||
pack: codeql/go-all
|
||||
extensible: sourceModel
|
||||
data:
|
||||
- ["github.com/nonexistent/test", "SEmbedPtrP1", True, "Source", "", "", "ReturnValue", "qltest", "manual"]
|
||||
- ["github.com/nonexistent/test", "SEmbedPtrP1", True, "SourceField", "", "", "", "qltest", "manual"]
|
||||
- addsTo:
|
||||
pack: codeql/go-all
|
||||
extensible: summaryModel
|
||||
data:
|
||||
- ["github.com/nonexistent/test", "SEmbedPtrP1", True, "Step", "", "", "Argument[0]", "ReturnValue", "value", "manual"]
|
||||
- addsTo:
|
||||
pack: codeql/go-all
|
||||
extensible: sinkModel
|
||||
data:
|
||||
- ["github.com/nonexistent/test", "SEmbedPtrP1", True, "Sink", "", "", "Argument[0]", "qltest", "manual"]
|
||||
- ["github.com/nonexistent/test", "SEmbedPtrP1", True, "SinkField", "", "", "", "qltest", "manual"]
|
||||
@@ -0,0 +1,26 @@
|
||||
import go
|
||||
import ModelValidation
|
||||
import TestUtilities.InlineExpectationsTest
|
||||
import MakeTest<FlowTest>
|
||||
|
||||
module Config implements DataFlow::ConfigSig {
|
||||
predicate isSource(DataFlow::Node source) { sourceNode(source, "qltest") }
|
||||
|
||||
predicate isSink(DataFlow::Node sink) { sinkNode(sink, "qltest") }
|
||||
}
|
||||
|
||||
module Flow = TaintTracking::Global<Config>;
|
||||
|
||||
module FlowTest implements TestSig {
|
||||
string getARelevantTag() { result = "SEmbedPtrP1[t]" }
|
||||
|
||||
predicate hasActualResult(Location location, string element, string tag, string value) {
|
||||
tag = "SEmbedPtrP1[t]" and
|
||||
exists(DataFlow::Node sink | Flow::flowTo(sink) |
|
||||
sink.hasLocationInfo(location.getFile().getAbsolutePath(), location.getStartLine(),
|
||||
location.getStartColumn(), location.getEndLine(), location.getEndColumn()) and
|
||||
element = sink.toString() and
|
||||
value = ""
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
testFailures
|
||||
invalidModelRow
|
||||
failures
|
||||
@@ -0,0 +1,16 @@
|
||||
extensions:
|
||||
- addsTo:
|
||||
pack: codeql/go-all
|
||||
extensible: sourceModel
|
||||
data:
|
||||
- ["github.com/nonexistent/test", "SEmbedPtrP2", True, "Source", "", "", "ReturnValue", "qltest", "manual"]
|
||||
- addsTo:
|
||||
pack: codeql/go-all
|
||||
extensible: summaryModel
|
||||
data:
|
||||
- ["github.com/nonexistent/test", "SEmbedPtrP2", True, "Step", "", "", "Argument[0]", "ReturnValue", "value", "manual"]
|
||||
- addsTo:
|
||||
pack: codeql/go-all
|
||||
extensible: sinkModel
|
||||
data:
|
||||
- ["github.com/nonexistent/test", "SEmbedPtrP2", True, "Sink", "", "", "Argument[0]", "qltest", "manual"]
|
||||
@@ -0,0 +1,26 @@
|
||||
import go
|
||||
import ModelValidation
|
||||
import TestUtilities.InlineExpectationsTest
|
||||
import MakeTest<FlowTest>
|
||||
|
||||
module Config implements DataFlow::ConfigSig {
|
||||
predicate isSource(DataFlow::Node source) { sourceNode(source, "qltest") }
|
||||
|
||||
predicate isSink(DataFlow::Node sink) { sinkNode(sink, "qltest") }
|
||||
}
|
||||
|
||||
module Flow = TaintTracking::Global<Config>;
|
||||
|
||||
module FlowTest implements TestSig {
|
||||
string getARelevantTag() { result = "SEmbedPtrP2[t]" }
|
||||
|
||||
predicate hasActualResult(Location location, string element, string tag, string value) {
|
||||
tag = "SEmbedPtrP2[t]" and
|
||||
exists(DataFlow::Node sink | Flow::flowTo(sink) |
|
||||
sink.hasLocationInfo(location.getFile().getAbsolutePath(), location.getStartLine(),
|
||||
location.getStartColumn(), location.getEndLine(), location.getEndColumn()) and
|
||||
element = sink.toString() and
|
||||
value = ""
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
testFailures
|
||||
invalidModelRow
|
||||
failures
|
||||
@@ -0,0 +1,18 @@
|
||||
extensions:
|
||||
- addsTo:
|
||||
pack: codeql/go-all
|
||||
extensible: sourceModel
|
||||
data:
|
||||
- ["github.com/nonexistent/test", "SEmbedPtrS1", True, "Source", "", "", "ReturnValue", "qltest", "manual"]
|
||||
- ["github.com/nonexistent/test", "SEmbedPtrS1", True, "SourceField", "", "", "", "qltest", "manual"]
|
||||
- addsTo:
|
||||
pack: codeql/go-all
|
||||
extensible: summaryModel
|
||||
data:
|
||||
- ["github.com/nonexistent/test", "SEmbedPtrS1", True, "Step", "", "", "Argument[0]", "ReturnValue", "value", "manual"]
|
||||
- addsTo:
|
||||
pack: codeql/go-all
|
||||
extensible: sinkModel
|
||||
data:
|
||||
- ["github.com/nonexistent/test", "SEmbedPtrS1", True, "Sink", "", "", "Argument[0]", "qltest", "manual"]
|
||||
- ["github.com/nonexistent/test", "SEmbedPtrS1", True, "SinkField", "", "", "", "qltest", "manual"]
|
||||
@@ -0,0 +1,26 @@
|
||||
import go
|
||||
import ModelValidation
|
||||
import TestUtilities.InlineExpectationsTest
|
||||
import MakeTest<FlowTest>
|
||||
|
||||
module Config implements DataFlow::ConfigSig {
|
||||
predicate isSource(DataFlow::Node source) { sourceNode(source, "qltest") }
|
||||
|
||||
predicate isSink(DataFlow::Node sink) { sinkNode(sink, "qltest") }
|
||||
}
|
||||
|
||||
module Flow = TaintTracking::Global<Config>;
|
||||
|
||||
module FlowTest implements TestSig {
|
||||
string getARelevantTag() { result = "SEmbedPtrS1[t]" }
|
||||
|
||||
predicate hasActualResult(Location location, string element, string tag, string value) {
|
||||
tag = "SEmbedPtrS1[t]" and
|
||||
exists(DataFlow::Node sink | Flow::flowTo(sink) |
|
||||
sink.hasLocationInfo(location.getFile().getAbsolutePath(), location.getStartLine(),
|
||||
location.getStartColumn(), location.getEndLine(), location.getEndColumn()) and
|
||||
element = sink.toString() and
|
||||
value = ""
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
testFailures
|
||||
invalidModelRow
|
||||
failures
|
||||
@@ -0,0 +1,16 @@
|
||||
extensions:
|
||||
- addsTo:
|
||||
pack: codeql/go-all
|
||||
extensible: sourceModel
|
||||
data:
|
||||
- ["github.com/nonexistent/test", "SEmbedPtrS2", True, "Source", "", "", "ReturnValue", "qltest", "manual"]
|
||||
- addsTo:
|
||||
pack: codeql/go-all
|
||||
extensible: summaryModel
|
||||
data:
|
||||
- ["github.com/nonexistent/test", "SEmbedPtrS2", True, "Step", "", "", "Argument[0]", "ReturnValue", "value", "manual"]
|
||||
- addsTo:
|
||||
pack: codeql/go-all
|
||||
extensible: sinkModel
|
||||
data:
|
||||
- ["github.com/nonexistent/test", "SEmbedPtrS2", True, "Sink", "", "", "Argument[0]", "qltest", "manual"]
|
||||
@@ -0,0 +1,26 @@
|
||||
import go
|
||||
import ModelValidation
|
||||
import TestUtilities.InlineExpectationsTest
|
||||
import MakeTest<FlowTest>
|
||||
|
||||
module Config implements DataFlow::ConfigSig {
|
||||
predicate isSource(DataFlow::Node source) { sourceNode(source, "qltest") }
|
||||
|
||||
predicate isSink(DataFlow::Node sink) { sinkNode(sink, "qltest") }
|
||||
}
|
||||
|
||||
module Flow = TaintTracking::Global<Config>;
|
||||
|
||||
module FlowTest implements TestSig {
|
||||
string getARelevantTag() { result = "SEmbedPtrS2[t]" }
|
||||
|
||||
predicate hasActualResult(Location location, string element, string tag, string value) {
|
||||
tag = "SEmbedPtrS2[t]" and
|
||||
exists(DataFlow::Node sink | Flow::flowTo(sink) |
|
||||
sink.hasLocationInfo(location.getFile().getAbsolutePath(), location.getStartLine(),
|
||||
location.getStartColumn(), location.getEndLine(), location.getEndColumn()) and
|
||||
element = sink.toString() and
|
||||
value = ""
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -3,7 +3,8 @@ extensions:
|
||||
pack: codeql/go-all
|
||||
extensible: sourceModel
|
||||
data:
|
||||
- ["github.com/nonexistent/test", "SEmbedS1", True, "Source", "", "", "ReturnValue", "remote", "manual"]
|
||||
- ["github.com/nonexistent/test", "SEmbedS1", True, "Source", "", "", "ReturnValue", "qltest", "manual"]
|
||||
- ["github.com/nonexistent/test", "SEmbedS1", True, "SourceField", "", "", "", "qltest", "manual"]
|
||||
- addsTo:
|
||||
pack: codeql/go-all
|
||||
extensible: summaryModel
|
||||
@@ -13,4 +14,5 @@ extensions:
|
||||
pack: codeql/go-all
|
||||
extensible: sinkModel
|
||||
data:
|
||||
- ["github.com/nonexistent/test", "SEmbedS1", True, "Sink", "", "", "Argument[0]", "path-injection", "manual"]
|
||||
- ["github.com/nonexistent/test", "SEmbedS1", True, "Sink", "", "", "Argument[0]", "qltest", "manual"]
|
||||
- ["github.com/nonexistent/test", "SEmbedS1", True, "SinkField", "", "", "", "qltest", "manual"]
|
||||
|
||||
@@ -1,14 +1,12 @@
|
||||
import go
|
||||
import semmle.go.dataflow.ExternalFlow
|
||||
import ModelValidation
|
||||
import semmle.go.dataflow.internal.FlowSummaryImpl as FlowSummaryImpl
|
||||
import TestUtilities.InlineExpectationsTest
|
||||
import MakeTest<FlowTest>
|
||||
|
||||
module Config implements DataFlow::ConfigSig {
|
||||
predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource }
|
||||
predicate isSource(DataFlow::Node source) { sourceNode(source, "qltest") }
|
||||
|
||||
predicate isSink(DataFlow::Node sink) { sink = any(FileSystemAccess fsa).getAPathArgument() }
|
||||
predicate isSink(DataFlow::Node sink) { sinkNode(sink, "qltest") }
|
||||
}
|
||||
|
||||
module Flow = TaintTracking::Global<Config>;
|
||||
|
||||
@@ -3,7 +3,7 @@ extensions:
|
||||
pack: codeql/go-all
|
||||
extensible: sourceModel
|
||||
data:
|
||||
- ["github.com/nonexistent/test", "SEmbedS2", True, "Source", "", "", "ReturnValue", "remote", "manual"]
|
||||
- ["github.com/nonexistent/test", "SEmbedS2", True, "Source", "", "", "ReturnValue", "qltest", "manual"]
|
||||
- addsTo:
|
||||
pack: codeql/go-all
|
||||
extensible: summaryModel
|
||||
@@ -13,4 +13,4 @@ extensions:
|
||||
pack: codeql/go-all
|
||||
extensible: sinkModel
|
||||
data:
|
||||
- ["github.com/nonexistent/test", "SEmbedS2", True, "Sink", "", "", "Argument[0]", "path-injection", "manual"]
|
||||
- ["github.com/nonexistent/test", "SEmbedS2", True, "Sink", "", "", "Argument[0]", "qltest", "manual"]
|
||||
|
||||
@@ -1,14 +1,12 @@
|
||||
import go
|
||||
import semmle.go.dataflow.ExternalFlow
|
||||
import ModelValidation
|
||||
import semmle.go.dataflow.internal.FlowSummaryImpl as FlowSummaryImpl
|
||||
import TestUtilities.InlineExpectationsTest
|
||||
import MakeTest<FlowTest>
|
||||
|
||||
module Config implements DataFlow::ConfigSig {
|
||||
predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource }
|
||||
predicate isSource(DataFlow::Node source) { sourceNode(source, "qltest") }
|
||||
|
||||
predicate isSink(DataFlow::Node sink) { sink = any(FileSystemAccess fsa).getAPathArgument() }
|
||||
predicate isSink(DataFlow::Node sink) { sinkNode(sink, "qltest") }
|
||||
}
|
||||
|
||||
module Flow = TaintTracking::Global<Config>;
|
||||
|
||||
@@ -3,7 +3,8 @@ extensions:
|
||||
pack: codeql/go-all
|
||||
extensible: sourceModel
|
||||
data:
|
||||
- ["github.com/nonexistent/test", "SImplEmbedI1", True, "Source", "", "", "ReturnValue", "remote", "manual"]
|
||||
- ["github.com/nonexistent/test", "SImplEmbedI1", True, "Source", "", "", "ReturnValue", "qltest", "manual"]
|
||||
- ["github.com/nonexistent/test", "SImplEmbedI1", True, "SourceField", "", "", "", "qltest", "manual"]
|
||||
- addsTo:
|
||||
pack: codeql/go-all
|
||||
extensible: summaryModel
|
||||
@@ -13,4 +14,5 @@ extensions:
|
||||
pack: codeql/go-all
|
||||
extensible: sinkModel
|
||||
data:
|
||||
- ["github.com/nonexistent/test", "SImplEmbedI1", True, "Sink", "", "", "Argument[0]", "path-injection", "manual"]
|
||||
- ["github.com/nonexistent/test", "SImplEmbedI1", True, "Sink", "", "", "Argument[0]", "qltest", "manual"]
|
||||
- ["github.com/nonexistent/test", "SImplEmbedI1", True, "SinkField", "", "", "", "qltest", "manual"]
|
||||
|
||||
@@ -1,14 +1,12 @@
|
||||
import go
|
||||
import semmle.go.dataflow.ExternalFlow
|
||||
import ModelValidation
|
||||
import semmle.go.dataflow.internal.FlowSummaryImpl as FlowSummaryImpl
|
||||
import TestUtilities.InlineExpectationsTest
|
||||
import MakeTest<FlowTest>
|
||||
|
||||
module Config implements DataFlow::ConfigSig {
|
||||
predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource }
|
||||
predicate isSource(DataFlow::Node source) { sourceNode(source, "qltest") }
|
||||
|
||||
predicate isSink(DataFlow::Node sink) { sink = any(FileSystemAccess fsa).getAPathArgument() }
|
||||
predicate isSink(DataFlow::Node sink) { sinkNode(sink, "qltest") }
|
||||
}
|
||||
|
||||
module Flow = TaintTracking::Global<Config>;
|
||||
|
||||
@@ -3,7 +3,7 @@ extensions:
|
||||
pack: codeql/go-all
|
||||
extensible: sourceModel
|
||||
data:
|
||||
- ["github.com/nonexistent/test", "SImplEmbedI2", True, "Source", "", "", "ReturnValue", "remote", "manual"]
|
||||
- ["github.com/nonexistent/test", "SImplEmbedI2", True, "Source", "", "", "ReturnValue", "qltest", "manual"]
|
||||
- addsTo:
|
||||
pack: codeql/go-all
|
||||
extensible: summaryModel
|
||||
@@ -13,4 +13,4 @@ extensions:
|
||||
pack: codeql/go-all
|
||||
extensible: sinkModel
|
||||
data:
|
||||
- ["github.com/nonexistent/test", "SImplEmbedI2", True, "Sink", "", "", "Argument[0]", "path-injection", "manual"]
|
||||
- ["github.com/nonexistent/test", "SImplEmbedI2", True, "Sink", "", "", "Argument[0]", "qltest", "manual"]
|
||||
|
||||
@@ -1,14 +1,12 @@
|
||||
import go
|
||||
import semmle.go.dataflow.ExternalFlow
|
||||
import ModelValidation
|
||||
import semmle.go.dataflow.internal.FlowSummaryImpl as FlowSummaryImpl
|
||||
import TestUtilities.InlineExpectationsTest
|
||||
import MakeTest<FlowTest>
|
||||
|
||||
module Config implements DataFlow::ConfigSig {
|
||||
predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource }
|
||||
predicate isSource(DataFlow::Node source) { sourceNode(source, "qltest") }
|
||||
|
||||
predicate isSink(DataFlow::Node sink) { sink = any(FileSystemAccess fsa).getAPathArgument() }
|
||||
predicate isSink(DataFlow::Node sink) { sinkNode(sink, "qltest") }
|
||||
}
|
||||
|
||||
module Flow = TaintTracking::Global<Config>;
|
||||
|
||||
@@ -3,7 +3,8 @@ extensions:
|
||||
pack: codeql/go-all
|
||||
extensible: sourceModel
|
||||
data:
|
||||
- ["github.com/nonexistent/test", "SImplEmbedS1", True, "Source", "", "", "ReturnValue", "remote", "manual"]
|
||||
- ["github.com/nonexistent/test", "SImplEmbedS1", True, "Source", "", "", "ReturnValue", "qltest", "manual"]
|
||||
- ["github.com/nonexistent/test", "SImplEmbedS1", True, "SourceField", "", "", "", "qltest", "manual"]
|
||||
- addsTo:
|
||||
pack: codeql/go-all
|
||||
extensible: summaryModel
|
||||
@@ -13,4 +14,5 @@ extensions:
|
||||
pack: codeql/go-all
|
||||
extensible: sinkModel
|
||||
data:
|
||||
- ["github.com/nonexistent/test", "SImplEmbedS1", True, "Sink", "", "", "Argument[0]", "path-injection", "manual"]
|
||||
- ["github.com/nonexistent/test", "SImplEmbedS1", True, "Sink", "", "", "Argument[0]", "qltest", "manual"]
|
||||
- ["github.com/nonexistent/test", "SImplEmbedS1", True, "SinkField", "", "", "", "qltest", "manual"]
|
||||
|
||||
@@ -1,14 +1,12 @@
|
||||
import go
|
||||
import semmle.go.dataflow.ExternalFlow
|
||||
import ModelValidation
|
||||
import semmle.go.dataflow.internal.FlowSummaryImpl as FlowSummaryImpl
|
||||
import TestUtilities.InlineExpectationsTest
|
||||
import MakeTest<FlowTest>
|
||||
|
||||
module Config implements DataFlow::ConfigSig {
|
||||
predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource }
|
||||
predicate isSource(DataFlow::Node source) { sourceNode(source, "qltest") }
|
||||
|
||||
predicate isSink(DataFlow::Node sink) { sink = any(FileSystemAccess fsa).getAPathArgument() }
|
||||
predicate isSink(DataFlow::Node sink) { sinkNode(sink, "qltest") }
|
||||
}
|
||||
|
||||
module Flow = TaintTracking::Global<Config>;
|
||||
|
||||
@@ -3,7 +3,7 @@ extensions:
|
||||
pack: codeql/go-all
|
||||
extensible: sourceModel
|
||||
data:
|
||||
- ["github.com/nonexistent/test", "SImplEmbedS2", True, "Source", "", "", "ReturnValue", "remote", "manual"]
|
||||
- ["github.com/nonexistent/test", "SImplEmbedS2", True, "Source", "", "", "ReturnValue", "qltest", "manual"]
|
||||
- addsTo:
|
||||
pack: codeql/go-all
|
||||
extensible: summaryModel
|
||||
@@ -13,4 +13,4 @@ extensions:
|
||||
pack: codeql/go-all
|
||||
extensible: sinkModel
|
||||
data:
|
||||
- ["github.com/nonexistent/test", "SImplEmbedS2", True, "Sink", "", "", "Argument[0]", "path-injection", "manual"]
|
||||
- ["github.com/nonexistent/test", "SImplEmbedS2", True, "Sink", "", "", "Argument[0]", "qltest", "manual"]
|
||||
|
||||
@@ -1,14 +1,12 @@
|
||||
import go
|
||||
import semmle.go.dataflow.ExternalFlow
|
||||
import ModelValidation
|
||||
import semmle.go.dataflow.internal.FlowSummaryImpl as FlowSummaryImpl
|
||||
import TestUtilities.InlineExpectationsTest
|
||||
import MakeTest<FlowTest>
|
||||
|
||||
module Config implements DataFlow::ConfigSig {
|
||||
predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource }
|
||||
predicate isSource(DataFlow::Node source) { sourceNode(source, "qltest") }
|
||||
|
||||
predicate isSink(DataFlow::Node sink) { sink = any(FileSystemAccess fsa).getAPathArgument() }
|
||||
predicate isSink(DataFlow::Node sink) { sinkNode(sink, "qltest") }
|
||||
}
|
||||
|
||||
module Flow = TaintTracking::Global<Config>;
|
||||
|
||||
@@ -1,14 +1,22 @@
|
||||
import go
|
||||
import semmle.go.dataflow.ExternalFlow
|
||||
import ModelValidation
|
||||
import semmle.go.dataflow.internal.FlowSummaryImpl as FlowSummaryImpl
|
||||
import TestUtilities.InlineExpectationsTest
|
||||
import MakeTest<FlowTest>
|
||||
|
||||
module Config implements DataFlow::ConfigSig {
|
||||
predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource }
|
||||
predicate isSource(DataFlow::Node source) {
|
||||
exists(Method m |
|
||||
m.hasQualifiedName("github.com/nonexistent/test", "I1", "Source") and
|
||||
source = m.getACall().getResult()
|
||||
)
|
||||
}
|
||||
|
||||
predicate isSink(DataFlow::Node sink) { sink = any(FileSystemAccess fsa).getAPathArgument() }
|
||||
predicate isSink(DataFlow::Node sink) {
|
||||
exists(Method m |
|
||||
m.hasQualifiedName("github.com/nonexistent/test", "I1", "Sink") and
|
||||
sink = m.getACall().getArgument(0)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
module Flow = TaintTracking::Global<Config>;
|
||||
@@ -27,15 +35,6 @@ module FlowTest implements TestSig {
|
||||
}
|
||||
}
|
||||
|
||||
class MySource extends RemoteFlowSource::Range instanceof DataFlow::Node {
|
||||
MySource() {
|
||||
exists(Method m |
|
||||
m.hasQualifiedName("github.com/nonexistent/test", "I1", "Source") and
|
||||
this = m.getACall().getResult()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
class MyStep extends DataFlow::FunctionModel, Method {
|
||||
MyStep() { this.hasQualifiedName("github.com/nonexistent/test", "I1", "Step") }
|
||||
|
||||
@@ -43,14 +42,3 @@ class MyStep extends DataFlow::FunctionModel, Method {
|
||||
input.isParameter(0) and output.isResult()
|
||||
}
|
||||
}
|
||||
|
||||
class MySink extends FileSystemAccess::Range, DataFlow::CallNode {
|
||||
MySink() {
|
||||
exists(Method m |
|
||||
m.hasQualifiedName("github.com/nonexistent/test", "I1", "Sink") and
|
||||
this = m.getACall()
|
||||
)
|
||||
}
|
||||
|
||||
override DataFlow::Node getAPathArgument() { result = this.getArgument(0) }
|
||||
}
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
testFailures
|
||||
invalidModelRow
|
||||
failures
|
||||
@@ -0,0 +1,54 @@
|
||||
import go
|
||||
import ModelValidation
|
||||
import TestUtilities.InlineExpectationsTest
|
||||
import MakeTest<FlowTest>
|
||||
|
||||
module Config implements DataFlow::ConfigSig {
|
||||
predicate isSource(DataFlow::Node source) {
|
||||
exists(Method m |
|
||||
m.hasQualifiedName("github.com/nonexistent/test", "P1", "Source") and
|
||||
source = m.getACall().getResult()
|
||||
)
|
||||
or
|
||||
exists(Field f |
|
||||
f.hasQualifiedName("github.com/nonexistent/test", "P1", "SourceField") and
|
||||
source = f.getARead()
|
||||
)
|
||||
}
|
||||
|
||||
predicate isSink(DataFlow::Node sink) {
|
||||
exists(Method m |
|
||||
m.hasQualifiedName("github.com/nonexistent/test", "P1", "Sink") and
|
||||
sink = m.getACall().getArgument(0)
|
||||
)
|
||||
or
|
||||
exists(Field f |
|
||||
f.hasQualifiedName("github.com/nonexistent/test", "P1", "SinkField") and
|
||||
any(DataFlow::Write w).writesField(_, f, sink)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
module Flow = TaintTracking::Global<Config>;
|
||||
|
||||
module FlowTest implements TestSig {
|
||||
string getARelevantTag() { result = "ql_P1" }
|
||||
|
||||
predicate hasActualResult(Location location, string element, string tag, string value) {
|
||||
tag = "ql_P1" and
|
||||
exists(DataFlow::Node sink | Flow::flowTo(sink) |
|
||||
sink.hasLocationInfo(location.getFile().getAbsolutePath(), location.getStartLine(),
|
||||
location.getStartColumn(), location.getEndLine(), location.getEndColumn()) and
|
||||
element = sink.toString() and
|
||||
value = ""
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
class MyStep extends DataFlow::FunctionModel, Method {
|
||||
MyStep() { this.hasQualifiedName("github.com/nonexistent/test", "P1", "Step") }
|
||||
|
||||
override predicate hasDataFlow(FunctionInput input, FunctionOutput output) {
|
||||
input.isParameter(0) and output.isResult()
|
||||
}
|
||||
}
|
||||
@@ -1,14 +1,32 @@
|
||||
import go
|
||||
import semmle.go.dataflow.ExternalFlow
|
||||
import ModelValidation
|
||||
import semmle.go.dataflow.internal.FlowSummaryImpl as FlowSummaryImpl
|
||||
import TestUtilities.InlineExpectationsTest
|
||||
import MakeTest<FlowTest>
|
||||
|
||||
module Config implements DataFlow::ConfigSig {
|
||||
predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource }
|
||||
predicate isSource(DataFlow::Node source) {
|
||||
exists(Method m |
|
||||
m.hasQualifiedName("github.com/nonexistent/test", "S1", "Source") and
|
||||
source = m.getACall().getResult()
|
||||
)
|
||||
or
|
||||
exists(Field f |
|
||||
f.hasQualifiedName("github.com/nonexistent/test", "S1", "SourceField") and
|
||||
source = f.getARead()
|
||||
)
|
||||
}
|
||||
|
||||
predicate isSink(DataFlow::Node sink) { sink = any(FileSystemAccess fsa).getAPathArgument() }
|
||||
predicate isSink(DataFlow::Node sink) {
|
||||
exists(Method m |
|
||||
m.hasQualifiedName("github.com/nonexistent/test", "S1", "Sink") and
|
||||
sink = m.getACall().getArgument(0)
|
||||
)
|
||||
or
|
||||
exists(Field f |
|
||||
f.hasQualifiedName("github.com/nonexistent/test", "S1", "SinkField") and
|
||||
any(DataFlow::Write w).writesField(_, f, sink)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
module Flow = TaintTracking::Global<Config>;
|
||||
@@ -27,15 +45,6 @@ module FlowTest implements TestSig {
|
||||
}
|
||||
}
|
||||
|
||||
class MySource extends RemoteFlowSource::Range instanceof DataFlow::Node {
|
||||
MySource() {
|
||||
exists(Method m |
|
||||
m.hasQualifiedName("github.com/nonexistent/test", "S1", "Source") and
|
||||
this = m.getACall().getResult()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
class MyStep extends DataFlow::FunctionModel, Method {
|
||||
MyStep() { this.hasQualifiedName("github.com/nonexistent/test", "S1", "Step") }
|
||||
|
||||
@@ -43,14 +52,3 @@ class MyStep extends DataFlow::FunctionModel, Method {
|
||||
input.isParameter(0) and output.isResult()
|
||||
}
|
||||
}
|
||||
|
||||
class MySink extends FileSystemAccess::Range, DataFlow::CallNode {
|
||||
MySink() {
|
||||
exists(Method m |
|
||||
m.hasQualifiedName("github.com/nonexistent/test", "S1", "Sink") and
|
||||
this = m.getACall()
|
||||
)
|
||||
}
|
||||
|
||||
override DataFlow::Node getAPathArgument() { result = this.getArgument(0) }
|
||||
}
|
||||
|
||||
@@ -1,89 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/nonexistent/test"
|
||||
)
|
||||
|
||||
func TestI1(t test.I1) {
|
||||
x := t.Source()
|
||||
y := t.Step(x)
|
||||
t.Sink(y) // $ I1[f] I1[t] ql_I1 SPURIOUS: IEmbedI1[t] SEmbedI1[t] ql_S1
|
||||
}
|
||||
|
||||
func TestI2(t test.I2) {
|
||||
x := t.Source()
|
||||
y := t.Step(x)
|
||||
t.Sink(y) // $ I1[t] I2[f] I2[t] SPURIOUS: IEmbedI1[t] IEmbedI2[t] SEmbedI1[t] SEmbedI2[t]
|
||||
}
|
||||
|
||||
func TestS1(t test.S1) {
|
||||
x := t.Source()
|
||||
y := t.Step(x)
|
||||
t.Sink(y) // $ I1[t] S1[f] S1[t] ql_S1 SPURIOUS: IEmbedI1[t] SEmbedI1[t] SEmbedS1[t]
|
||||
}
|
||||
|
||||
func TestS2(t test.S2) {
|
||||
x := t.Source()
|
||||
y := t.Step(x)
|
||||
t.Sink(y) // $ I1[t] I2[t] SPURIOUS: IEmbedI1[t] IEmbedI2[t] SEmbedI1[t] SEmbedI2[t] SEmbedS2[t]
|
||||
}
|
||||
|
||||
func TestSEmbedI1(t test.SEmbedI1) {
|
||||
x := t.Source()
|
||||
y := t.Step(x)
|
||||
t.Sink(y) // $ I1[t] SEmbedI1[t] ql_I1 SPURIOUS: I1[f] IEmbedI1[t] ql_S1
|
||||
}
|
||||
|
||||
func TestSEmbedI2(t test.SEmbedI2) {
|
||||
x := t.Source()
|
||||
y := t.Step(x)
|
||||
t.Sink(y) // $ I1[t] I2[t] SEmbedI2[t] SPURIOUS: I2[f] IEmbedI1[t] IEmbedI2[t] SEmbedI1[t]
|
||||
}
|
||||
|
||||
func TestIEmbedI1(t test.IEmbedI1) {
|
||||
x := t.Source()
|
||||
y := t.Step(x)
|
||||
t.Sink(y) // $ I1[t] IEmbedI1[t] ql_I1 SPURIOUS: I1[f] SEmbedI1[t] ql_S1
|
||||
}
|
||||
|
||||
func TestIEmbedI2(t test.IEmbedI2) {
|
||||
x := t.Source()
|
||||
y := t.Step(x)
|
||||
t.Sink(y) // $ I1[t] I2[t] IEmbedI2[t] SPURIOUS: I2[f] IEmbedI1[t] SEmbedI1[t] SEmbedI2[t]
|
||||
}
|
||||
|
||||
func TestSImplEmbedI1(t test.SImplEmbedI1) {
|
||||
x := t.Source()
|
||||
y := t.Step(x)
|
||||
t.Sink(y) // $ I1[t] SImplEmbedI1[t] SPURIOUS: IEmbedI1[t] SEmbedI1[t]
|
||||
}
|
||||
|
||||
func TestSImplEmbedI2(t test.SImplEmbedI2) {
|
||||
x := t.Source()
|
||||
y := t.Step(x)
|
||||
t.Sink(y) // $ I1[t] I2[t] SImplEmbedI2[t] SPURIOUS: IEmbedI1[t] IEmbedI2[t] SEmbedI1[t] SEmbedI2[t]
|
||||
}
|
||||
|
||||
func TestSEmbedS1(t test.SEmbedS1) {
|
||||
x := t.Source()
|
||||
y := t.Step(x)
|
||||
t.Sink(y) // $ I1[t] S1[t] SEmbedS1[t] ql_S1 SPURIOUS: IEmbedI1[t] S1[f] SEmbedI1[t]
|
||||
}
|
||||
|
||||
func TestSEmbedS2(t test.SEmbedS2) {
|
||||
x := t.Source()
|
||||
y := t.Step(x)
|
||||
t.Sink(y) // $ I1[t] I2[t] SEmbedS2[t] SPURIOUS: IEmbedI1[t] IEmbedI2[t] SEmbedI1[t] SEmbedI2[t]
|
||||
}
|
||||
|
||||
func TestSImplEmbedS1(t test.SImplEmbedS1) {
|
||||
x := t.Source()
|
||||
y := t.Step(x)
|
||||
t.Sink(y) // $ I1[t] SImplEmbedS1[t] SPURIOUS: IEmbedI1[t] SEmbedI1[t]
|
||||
}
|
||||
|
||||
func TestSImplEmbedS2(t test.SImplEmbedS2) {
|
||||
x := t.Source()
|
||||
y := t.Step(x)
|
||||
t.Sink(y) // $ I1[t] I2[t] SImplEmbedS2[t] SPURIOUS: IEmbedI1[t] IEmbedI2[t] SEmbedI1[t] SEmbedI2[t]
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/nonexistent/test"
|
||||
)
|
||||
|
||||
func TestFieldsP1(t test.P1) {
|
||||
a := t.SourceField
|
||||
t.SinkField = a // $ P1[f] P1[t] ql_P1 SPURIOUS: ql_S1
|
||||
}
|
||||
|
||||
func TestFieldsS1(t test.S1) {
|
||||
a := t.SourceField
|
||||
t.SinkField = a // $ S1[f] S1[t] ql_S1 SPURIOUS: ql_P1
|
||||
}
|
||||
|
||||
func TestFieldsSEmbedI1(t test.SEmbedI1) {
|
||||
a := t.SourceField
|
||||
t.SinkField = a // $ SEmbedI1[t]
|
||||
}
|
||||
|
||||
func TestFieldsSImplEmbedI1(t test.SImplEmbedI1) {
|
||||
a := t.SourceField
|
||||
t.SinkField = a // $ SImplEmbedI1[t]
|
||||
}
|
||||
|
||||
func TestFieldsPImplEmbedI1(t test.PImplEmbedI1) {
|
||||
a := t.SourceField
|
||||
t.SinkField = a // $ PImplEmbedI1[t]
|
||||
}
|
||||
|
||||
func TestFieldsSEmbedP1(t test.SEmbedP1) {
|
||||
a := t.SourceField
|
||||
t.SinkField = a // $ P1[t] SEmbedP1[t] ql_P1 SPURIOUS: ql_S1
|
||||
}
|
||||
|
||||
func TestFieldsSEmbedS1(t test.SEmbedS1) {
|
||||
a := t.SourceField
|
||||
t.SinkField = a // $ S1[t] SEmbedS1[t] ql_S1 SPURIOUS: ql_P1
|
||||
}
|
||||
|
||||
func TestFieldsSEmbedPtrP1(t test.SEmbedPtrP1) {
|
||||
a := t.SourceField
|
||||
t.SinkField = a // $ P1[t] SEmbedPtrP1[t] ql_P1 SPURIOUS: ql_S1
|
||||
}
|
||||
|
||||
func TestFieldsSEmbedPtrS1(t test.SEmbedPtrS1) {
|
||||
a := t.SourceField
|
||||
t.SinkField = a // $ S1[t] SEmbedPtrS1[t] ql_S1 SPURIOUS: ql_P1
|
||||
}
|
||||
|
||||
func TestFieldsSImplEmbedS1(t test.SImplEmbedS1) {
|
||||
a := t.SourceField
|
||||
t.SinkField = a // $ SImplEmbedS1[t]
|
||||
}
|
||||
|
||||
func TestFieldsSEmbedSEmbedI1(t test.SEmbedSEmbedI1) {
|
||||
a := t.SourceField
|
||||
t.SinkField = a
|
||||
}
|
||||
|
||||
func TestFieldsSEmbedSEmbedS1(t test.SEmbedSEmbedS1) {
|
||||
a := t.SourceField
|
||||
t.SinkField = a // $ S1[t] SEmbedS1[t] ql_S1 SPURIOUS: ql_P1
|
||||
}
|
||||
|
||||
func TestFieldsSEmbedSEmbedPtrS1(t test.SEmbedSEmbedPtrS1) {
|
||||
a := t.SourceField
|
||||
t.SinkField = a // $ S1[t] SEmbedPtrS1[t] ql_S1 SPURIOUS: ql_P1
|
||||
}
|
||||
|
||||
func TestFieldsSEmbedPtrSEmbedS1(t test.SEmbedPtrSEmbedS1) {
|
||||
a := t.SourceField
|
||||
t.SinkField = a // $ S1[t] SEmbedS1[t] ql_S1 SPURIOUS: ql_P1
|
||||
}
|
||||
|
||||
func TestFieldsSEmbedPtrSEmbedPtrS1(t test.SEmbedPtrSEmbedPtrS1) {
|
||||
a := t.SourceField
|
||||
t.SinkField = a // $ S1[t] SEmbedPtrS1[t] ql_S1 SPURIOUS: ql_P1
|
||||
}
|
||||
|
||||
func TestFieldsSEmbedS1AndSEmbedS1(t test.SEmbedS1AndSEmbedS1) {
|
||||
a := t.SourceField
|
||||
t.SinkField = a // $ S1[t] ql_S1 SPURIOUS: ql_P1
|
||||
}
|
||||
@@ -0,0 +1,173 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/nonexistent/test"
|
||||
)
|
||||
|
||||
func TestMethodsI1(t test.I1) {
|
||||
x := t.Source()
|
||||
y := t.Step(x)
|
||||
t.Sink(y) // $ I1[f] I1[t] ql_I1 SPURIOUS: ql_P1 ql_S1
|
||||
}
|
||||
|
||||
func TestMethodsI2(t test.I2) {
|
||||
x := t.Source()
|
||||
y := t.Step(x)
|
||||
t.Sink(y) // $ I1[t] I2[f] I2[t]
|
||||
}
|
||||
|
||||
func TestMethodsS1(t test.S1) {
|
||||
x := t.Source()
|
||||
y := t.Step(x)
|
||||
t.Sink(y) // $ I1[t] S1[f] S1[t] ql_S1
|
||||
}
|
||||
|
||||
func TestMethodsP1(t test.P1) {
|
||||
x := t.Source()
|
||||
y := t.Step(x)
|
||||
t.Sink(y) // $ I1[t] P1[f] P1[t] ql_P1
|
||||
}
|
||||
|
||||
func TestMethodsS2(t test.S2) {
|
||||
x := t.Source()
|
||||
y := t.Step(x)
|
||||
t.Sink(y) // $ I1[t] I2[t]
|
||||
}
|
||||
|
||||
func TestMethodsP2(t test.P2) {
|
||||
x := t.Source()
|
||||
y := t.Step(x)
|
||||
t.Sink(y) // $ I1[t] I2[t]
|
||||
}
|
||||
|
||||
func TestMethodsSEmbedI1(t test.SEmbedI1) {
|
||||
x := t.Source()
|
||||
y := t.Step(x)
|
||||
t.Sink(y) // $ I1[t] SEmbedI1[t] ql_I1 SPURIOUS: ql_P1 ql_S1
|
||||
}
|
||||
|
||||
func TestMethodsSEmbedI2(t test.SEmbedI2) {
|
||||
x := t.Source()
|
||||
y := t.Step(x)
|
||||
t.Sink(y) // $ I1[t] I2[t] SEmbedI2[t]
|
||||
}
|
||||
|
||||
func TestMethodsIEmbedI1(t test.IEmbedI1) {
|
||||
x := t.Source()
|
||||
y := t.Step(x)
|
||||
t.Sink(y) // $ I1[t] IEmbedI1[t] ql_I1 SPURIOUS: ql_P1 ql_S1
|
||||
}
|
||||
|
||||
func TestMethodsIEmbedI2(t test.IEmbedI2) {
|
||||
x := t.Source()
|
||||
y := t.Step(x)
|
||||
t.Sink(y) // $ I1[t] I2[t] IEmbedI2[t]
|
||||
}
|
||||
|
||||
func TestMethodsSImplEmbedI1(t test.SImplEmbedI1) {
|
||||
x := t.Source()
|
||||
y := t.Step(x)
|
||||
t.Sink(y) // $ I1[t] SImplEmbedI1[t]
|
||||
}
|
||||
|
||||
func TestMethodsSImplEmbedI2(t test.SImplEmbedI2) {
|
||||
x := t.Source()
|
||||
y := t.Step(x)
|
||||
t.Sink(y) // $ I1[t] I2[t] SImplEmbedI2[t]
|
||||
}
|
||||
|
||||
func TestMethodsPImplEmbedI1(t test.PImplEmbedI1) {
|
||||
x := t.Source()
|
||||
y := t.Step(x)
|
||||
t.Sink(y) // $ I1[t] PImplEmbedI1[t]
|
||||
}
|
||||
|
||||
func TestMethodsPImplEmbedI2(t test.PImplEmbedI2) {
|
||||
x := t.Source()
|
||||
y := t.Step(x)
|
||||
t.Sink(y) // $ I1[t] I2[t] PImplEmbedI2[t]
|
||||
}
|
||||
|
||||
func TestMethodsSEmbedS1(t test.SEmbedS1) {
|
||||
x := t.Source()
|
||||
y := t.Step(x)
|
||||
t.Sink(y) // $ I1[t] S1[t] SEmbedS1[t] ql_S1
|
||||
}
|
||||
|
||||
func TestMethodsSEmbedS2(t test.SEmbedS2) {
|
||||
x := t.Source()
|
||||
y := t.Step(x)
|
||||
t.Sink(y) // $ I1[t] I2[t] SEmbedS2[t]
|
||||
}
|
||||
|
||||
func TestMethodsSEmbedPtrS1(t test.SEmbedPtrS1) {
|
||||
x := t.Source()
|
||||
y := t.Step(x)
|
||||
t.Sink(y) // $ I1[t] S1[t] SEmbedPtrS1[t] ql_S1
|
||||
}
|
||||
|
||||
func TestMethodsSEmbedPtrS2(t test.SEmbedPtrS2) {
|
||||
x := t.Source()
|
||||
y := t.Step(x)
|
||||
t.Sink(y) // $ I1[t] I2[t] SEmbedPtrS2[t]
|
||||
}
|
||||
|
||||
func TestMethodsSEmbedP1(t *test.SEmbedP1) {
|
||||
x := t.Source()
|
||||
y := t.Step(x)
|
||||
t.Sink(y) // $ I1[t] P1[t] SEmbedP1[t] ql_P1
|
||||
}
|
||||
|
||||
func TestMethodsSEmbedP2(t *test.SEmbedP2) {
|
||||
x := t.Source()
|
||||
y := t.Step(x)
|
||||
t.Sink(y) // $ I1[t] I2[t] SEmbedP2[t]
|
||||
}
|
||||
|
||||
func TestMethodsSImplEmbedS1(t test.SImplEmbedS1) {
|
||||
x := t.Source()
|
||||
y := t.Step(x)
|
||||
t.Sink(y) // $ I1[t] SImplEmbedS1[t]
|
||||
}
|
||||
|
||||
func TestMethodsSImplEmbedS2(t test.SImplEmbedS2) {
|
||||
x := t.Source()
|
||||
y := t.Step(x)
|
||||
t.Sink(y) // $ I1[t] I2[t] SImplEmbedS2[t]
|
||||
}
|
||||
|
||||
func TestMethodsSEmbedSEmbedI1(t test.SEmbedSEmbedI1) {
|
||||
x := t.Source()
|
||||
y := t.Step(x)
|
||||
t.Sink(y) // $ I1[t] SEmbedI1[t] ql_I1 SPURIOUS: ql_P1 ql_S1
|
||||
}
|
||||
|
||||
func TestMethodsSEmbedSEmbedS1(t test.SEmbedSEmbedS1) {
|
||||
x := t.Source()
|
||||
y := t.Step(x)
|
||||
t.Sink(y) // $ I1[t] S1[t] SEmbedS1[t] ql_S1
|
||||
}
|
||||
|
||||
func TestMethodsSEmbedSEmbedPtrS1(t test.SEmbedSEmbedPtrS1) {
|
||||
x := t.Source()
|
||||
y := t.Step(x)
|
||||
t.Sink(y) // $ I1[t] S1[t] SEmbedPtrS1[t] ql_S1
|
||||
}
|
||||
|
||||
func TestMethodsSEmbedPtrSEmbedS1(t test.SEmbedPtrSEmbedS1) {
|
||||
x := t.Source()
|
||||
y := t.Step(x)
|
||||
t.Sink(y) // $ I1[t] S1[t] SEmbedS1[t] ql_S1
|
||||
}
|
||||
|
||||
func TestMethodsSEmbedPtrSEmbedPtrS1(t test.SEmbedPtrSEmbedPtrS1) {
|
||||
x := t.Source()
|
||||
y := t.Step(x)
|
||||
t.Sink(y) // $ I1[t] S1[t] SEmbedPtrS1[t] ql_S1
|
||||
}
|
||||
|
||||
func TestMethodsSEmbedS1AndSEmbedS1(t test.SEmbedS1AndSEmbedS1) {
|
||||
x := t.Source()
|
||||
y := t.Step(x)
|
||||
t.Sink(y) // $ I1[t] S1[t] ql_S1
|
||||
}
|
||||
@@ -16,35 +16,73 @@ type I2 interface {
|
||||
}
|
||||
|
||||
// A struct type implementing I1
|
||||
type S1 struct{}
|
||||
type S1 struct {
|
||||
SourceField string
|
||||
SinkField string
|
||||
}
|
||||
|
||||
func (t *S1) Source() interface{} {
|
||||
func (t S1) Source() interface{} {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *S1) Sink(interface{}) {}
|
||||
func (t S1) Sink(interface{}) {}
|
||||
|
||||
func (t *S1) Step(val interface{}) interface{} {
|
||||
func (t S1) Step(val interface{}) interface{} {
|
||||
return val
|
||||
}
|
||||
|
||||
// A struct type whose pointer type implements I1
|
||||
type P1 struct {
|
||||
SourceField string
|
||||
SinkField string
|
||||
}
|
||||
|
||||
func (t *P1) Source() interface{} {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *P1) Sink(interface{}) {}
|
||||
|
||||
func (t *P1) Step(val interface{}) interface{} {
|
||||
return val
|
||||
}
|
||||
|
||||
// A struct type implementing I2
|
||||
type S2 struct{}
|
||||
|
||||
func (t *S2) Source() interface{} {
|
||||
func (t S2) Source() interface{} {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *S2) Sink(interface{}) {}
|
||||
func (t S2) Sink(interface{}) {}
|
||||
|
||||
func (t *S2) Step(val interface{}) interface{} {
|
||||
func (t S2) Step(val interface{}) interface{} {
|
||||
return val
|
||||
}
|
||||
|
||||
func (t *S2) ExtraMethodI2() {}
|
||||
func (t S2) ExtraMethodI2() {}
|
||||
|
||||
// A struct type whose pointer type implements I2
|
||||
type P2 struct{}
|
||||
|
||||
func (t *P2) Source() interface{} {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *P2) Sink(interface{}) {}
|
||||
|
||||
func (t *P2) Step(val interface{}) interface{} {
|
||||
return val
|
||||
}
|
||||
|
||||
func (t *P2) ExtraMethodI2() {}
|
||||
|
||||
// A struct type embedding I1
|
||||
type SEmbedI1 struct{ I1 }
|
||||
type SEmbedI1 struct {
|
||||
I1
|
||||
SourceField string
|
||||
SinkField string
|
||||
}
|
||||
|
||||
// A struct type embedding I2
|
||||
type SEmbedI2 struct{ I2 }
|
||||
@@ -63,15 +101,19 @@ type IEmbedI2 interface {
|
||||
|
||||
// A struct type embedding I1 and separately implementing its methods, so the
|
||||
// methods of the embedded field are not promoted.
|
||||
type SImplEmbedI1 struct{ I1 }
|
||||
type SImplEmbedI1 struct {
|
||||
I1
|
||||
SourceField string
|
||||
SinkField string
|
||||
}
|
||||
|
||||
func (t *SImplEmbedI1) Source() interface{} {
|
||||
func (t SImplEmbedI1) Source() interface{} {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *SImplEmbedI1) Sink(interface{}) {}
|
||||
func (t SImplEmbedI1) Sink(interface{}) {}
|
||||
|
||||
func (t *SImplEmbedI1) Step(val interface{}) interface{} {
|
||||
func (t SImplEmbedI1) Step(val interface{}) interface{} {
|
||||
return val
|
||||
}
|
||||
|
||||
@@ -79,17 +121,51 @@ func (t *SImplEmbedI1) Step(val interface{}) interface{} {
|
||||
// methods of the embedded field are not promoted.
|
||||
type SImplEmbedI2 struct{ I2 }
|
||||
|
||||
func (t *SImplEmbedI2) Source() interface{} {
|
||||
func (t SImplEmbedI2) Source() interface{} {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *SImplEmbedI2) Sink(interface{}) {}
|
||||
func (t SImplEmbedI2) Sink(interface{}) {}
|
||||
|
||||
func (t *SImplEmbedI2) Step(val interface{}) interface{} {
|
||||
func (t SImplEmbedI2) Step(val interface{}) interface{} {
|
||||
return val
|
||||
}
|
||||
|
||||
func (t *SImplEmbedI2) ExtraMethodI2() {}
|
||||
func (t SImplEmbedI2) ExtraMethodI2() {}
|
||||
|
||||
// A struct type embedding I1 and separately implementing its methods on its
|
||||
// pointer type, so the methods of the embedded field are not promoted.
|
||||
type PImplEmbedI1 struct {
|
||||
I1
|
||||
SourceField string
|
||||
SinkField string
|
||||
}
|
||||
|
||||
func (t *PImplEmbedI1) Source() interface{} {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *PImplEmbedI1) Sink(interface{}) {}
|
||||
|
||||
func (t *PImplEmbedI1) Step(val interface{}) interface{} {
|
||||
return val
|
||||
}
|
||||
|
||||
// A struct type embedding I2 and separately implementing its methods, so the
|
||||
// methods of the embedded field are not promoted.
|
||||
type PImplEmbedI2 struct{ I2 }
|
||||
|
||||
func (t *PImplEmbedI2) Source() interface{} {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *PImplEmbedI2) Sink(interface{}) {}
|
||||
|
||||
func (t *PImplEmbedI2) Step(val interface{}) interface{} {
|
||||
return val
|
||||
}
|
||||
|
||||
func (t *PImplEmbedI2) ExtraMethodI2() {}
|
||||
|
||||
// A struct type embedding S1
|
||||
type SEmbedS1 struct{ S1 }
|
||||
@@ -97,9 +173,31 @@ type SEmbedS1 struct{ S1 }
|
||||
// A struct type embedding S2
|
||||
type SEmbedS2 struct{ S2 }
|
||||
|
||||
// A struct type embedding S1 and separately implementing I1's methods, so the
|
||||
// methods of the embedded field are not promoted.
|
||||
type SImplEmbedS1 struct{ S1 }
|
||||
// A struct type embedding P1
|
||||
type SEmbedP1 struct{ P1 }
|
||||
|
||||
// A struct type embedding P2
|
||||
type SEmbedP2 struct{ P2 }
|
||||
|
||||
// A struct type embedding *S1
|
||||
type SEmbedPtrS1 struct{ *S1 }
|
||||
|
||||
// A struct type embedding *S2
|
||||
type SEmbedPtrS2 struct{ *S2 }
|
||||
|
||||
// A struct type embedding *P1
|
||||
type SEmbedPtrP1 struct{ *P1 }
|
||||
|
||||
// A struct type embedding *P2
|
||||
type SEmbedPtrP2 struct{ *P2 }
|
||||
|
||||
// A struct type embedding S1 and separately implementing I1's methods and
|
||||
// fields, so the methods and fields of the embedded field are not promoted.
|
||||
type SImplEmbedS1 struct {
|
||||
S1
|
||||
SourceField string
|
||||
SinkField string
|
||||
}
|
||||
|
||||
func (t *SImplEmbedS1) Source() interface{} {
|
||||
return nil
|
||||
@@ -126,3 +224,28 @@ func (t *SImplEmbedS2) Step(val interface{}) interface{} {
|
||||
}
|
||||
|
||||
func (t *SImplEmbedS2) ExtraMethodI2() {}
|
||||
|
||||
// A struct type embedding SEmbedI1
|
||||
type SEmbedSEmbedI1 struct {
|
||||
SEmbedI1
|
||||
SourceField string
|
||||
SinkField string
|
||||
}
|
||||
|
||||
// A struct type embedding SEmbedS1
|
||||
type SEmbedSEmbedS1 struct{ SEmbedS1 }
|
||||
|
||||
// A struct type embedding SEmbedPtrS1
|
||||
type SEmbedSEmbedPtrS1 struct{ SEmbedPtrS1 }
|
||||
|
||||
// A struct type embedding SEmbedS1
|
||||
type SEmbedPtrSEmbedS1 struct{ *SEmbedS1 }
|
||||
|
||||
// A struct type embedding SEmbedPtrS1
|
||||
type SEmbedPtrSEmbedPtrS1 struct{ *SEmbedPtrS1 }
|
||||
|
||||
// A struct type embedding S1 and SEmbedS1
|
||||
type SEmbedS1AndSEmbedS1 struct {
|
||||
S1
|
||||
SEmbedS1
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user