Skip to content

Commit 6f91cc1

Browse files
authored
Merge pull request #719 from owen-mc/bugfix/find-callee-through-function-variables
Look for callees through function variables
2 parents a8eeef6 + 5f3b913 commit 6f91cc1

File tree

6 files changed

+36
-5
lines changed

6 files changed

+36
-5
lines changed
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
---
2+
category: minorAnalysis
3+
---
4+
* The method predicate `getACalleeIncludingExternals` on `DataFlow::CallNode` and the function `viableCallable` in `DataFlowDispatch` now also work for calls to functions via a variable, where the function can be determined using local flow.

ql/lib/semmle/go/Expr.qll

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -784,7 +784,14 @@ class CallExpr extends CallOrConversionExpr {
784784
/** Gets the number of argument expressions of this call. */
785785
int getNumArgument() { result = count(this.getAnArgument()) }
786786

787-
/** Gets the name of the invoked function or method if it can be determined syntactically. */
787+
/**
788+
* Gets the name of the invoked function, method or variable if it can be
789+
* determined syntactically.
790+
*
791+
* Note that if a variable is being called then this gets the variable name
792+
* rather than the name of the function or method that has been assigned to
793+
* the variable.
794+
*/
788795
string getCalleeName() {
789796
exists(Expr callee | callee = this.getCalleeExpr().stripParens() |
790797
result = callee.(Ident).getName()

ql/lib/semmle/go/dataflow/internal/DataFlowDispatch.qll

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ private predicate isInterfaceCallReceiver(
1010
) {
1111
call.getReceiver() = recv and
1212
recv.getType().getUnderlyingType() = tp and
13-
m = call.getCalleeName()
13+
m = call.getACalleeIncludingExternals().asFunction().getName()
1414
}
1515

1616
/** Gets a data-flow node that may flow into the receiver value of `call`, which is an interface value. */

ql/lib/semmle/go/dataflow/internal/DataFlowNodes.qll

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -470,6 +470,8 @@ module Public {
470470
exists(DataFlow::Node calleeSource | calleeSource = this.getACalleeSource() |
471471
result.asFuncLit() = calleeSource.asExpr()
472472
or
473+
calleeSource = result.asFunction().getARead()
474+
or
473475
exists(Method declared, Method actual |
474476
calleeSource = declared.getARead() and
475477
actual.implements(declared) and
@@ -484,8 +486,15 @@ module Public {
484486
*/
485487
FuncDef getACallee() { result = this.getACalleeIncludingExternals().getFuncDef() }
486488

487-
/** Gets the name of the function or method being called, if it can be determined. */
488-
string getCalleeName() { result = expr.getTarget().getName() or result = expr.getCalleeName() }
489+
/**
490+
* Gets the name of the function, method or variable that is being called.
491+
*
492+
* Note that if we are calling a variable then this gets the variable name.
493+
* It does not attempt to get the name of the function or method that is
494+
* assigned to the variable. To do that, use
495+
* `getACalleeIncludingExternals().asFunction().getName()`.
496+
*/
497+
string getCalleeName() { result = expr.getCalleeName() }
489498

490499
/** Gets the data flow node specifying the function to be called. */
491500
Node getCalleeNode() { result = DataFlow::exprNode(expr.getCalleeExpr()) }

ql/test/library-tests/semmle/go/dataflow/CallGraph/getACallee.expected

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,3 +4,4 @@ spuriousCallee
44
| main.go:44:3:44:7 | call to m | main.go:21:1:21:20 | function declaration |
55
| main.go:56:2:56:6 | call to m | main.go:21:1:21:20 | function declaration |
66
| test.go:42:2:42:13 | call to Write | test.go:36:1:39:1 | function declaration |
7+
| test.go:44:2:44:8 | call to f1 | test.go:36:1:39:1 | function declaration |

ql/test/library-tests/semmle/go/dataflow/CallGraph/test.go

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,10 +40,20 @@ func (_ MockWriter) Write(p []byte) (n int, err error) { // name: MockWriter.Wri
4040

4141
func test5(h hash.Hash, w io.Writer) { // name: test5
4242
h.Write(nil) // callee: MockHash.Write
43+
f1 := h.Write
44+
f1(nil) // callee: MockHash.Write
45+
4346
w.Write(nil) // callee: MockWriter.Write callee: MockHash.Write
44-
h.Reset() // callee: Resetter.Reset
47+
f2 := w.Write
48+
f2(nil) // callee: MockWriter.Write callee: MockHash.Write
49+
50+
h.Reset() // callee: Resetter.Reset
51+
f3 := h.Reset
52+
f3() // callee: Resetter.Reset
4553
}
4654

4755
func test6(h MockHash, w MockWriter) {
4856
test5(h, w) // callee: test5
57+
f := test5
58+
f(h, w) // callee: test5
4959
}

0 commit comments

Comments
 (0)