diff --git a/cpp/ql/lib/semmle/code/cpp/ir/internal/IRCppLanguage.qll b/cpp/ql/lib/semmle/code/cpp/ir/internal/IRCppLanguage.qll index 681e2838ffbe..28bbd40f8bf5 100644 --- a/cpp/ql/lib/semmle/code/cpp/ir/internal/IRCppLanguage.qll +++ b/cpp/ql/lib/semmle/code/cpp/ir/internal/IRCppLanguage.qll @@ -67,8 +67,13 @@ class Class = Cpp::Class; // Used for inheritance conversions predicate hasCaseEdge(string minValue, string maxValue) { hasCaseEdge(_, minValue, maxValue) } predicate hasPositionalArgIndex(int argIndex) { - exists(Cpp::FunctionCall call | exists(call.getArgument(argIndex))) or + exists(Cpp::FunctionCall call | exists(call.getArgument(argIndex))) + or exists(Cpp::BuiltInOperation op | exists(op.getChild(argIndex))) + or + // Ensure we are always able to output the argument of a call to the delete operator. + exists(Cpp::DeleteExpr d) and + argIndex = 0 } predicate hasAsmOperandIndex(int operandIndex) { diff --git a/cpp/ql/test/library-tests/ir/no-function-calls/PrintAST.expected b/cpp/ql/test/library-tests/ir/no-function-calls/PrintAST.expected new file mode 100644 index 000000000000..7f15878cf20e --- /dev/null +++ b/cpp/ql/test/library-tests/ir/no-function-calls/PrintAST.expected @@ -0,0 +1,36 @@ +#-----| [CopyAssignmentOperator] __va_list_tag& __va_list_tag::operator=(__va_list_tag const&) +#-----| : +#-----| getParameter(0): [Parameter] (unnamed parameter 0) +#-----| Type = [LValueReferenceType] const __va_list_tag & +#-----| [MoveAssignmentOperator] __va_list_tag& __va_list_tag::operator=(__va_list_tag&&) +#-----| : +#-----| getParameter(0): [Parameter] (unnamed parameter 0) +#-----| Type = [RValueReferenceType] __va_list_tag && +#-----| [Operator,TopLevelFunction] void operator delete(void*) +#-----| : +#-----| getParameter(0): [Parameter] (unnamed parameter 0) +#-----| Type = [VoidPointerType] void * +test.cpp: +# 5| [TopLevelFunction] void foo(int*) +# 5| : +# 5| getParameter(0): [Parameter] x +# 5| Type = [IntPointerType] int * +# 5| getEntryPoint(): [BlockStmt] { ... } +# 6| getStmt(0): [ExprStmt] ExprStmt +# 6| getExpr(): [DeleteExpr] delete +# 6| Type = [VoidType] void +# 6| ValueCategory = prvalue +# 6| getExprWithReuse(): [VariableAccess] x +# 6| Type = [IntPointerType] int * +# 6| ValueCategory = prvalue(load) +# 7| getStmt(1): [ReturnStmt] return ... +# 9| [TopLevelFunction] void bar() +# 9| : +# 11| [TopLevelFunction] void jazz() +# 11| : +# 11| getEntryPoint(): [BlockStmt] { ... } +# 12| getStmt(0): [ExprStmt] ExprStmt +# 12| getExpr(): [FunctionCall] call to bar +# 12| Type = [VoidType] void +# 12| ValueCategory = prvalue +# 13| getStmt(1): [ReturnStmt] return ... diff --git a/cpp/ql/test/library-tests/ir/no-function-calls/PrintAST.ql b/cpp/ql/test/library-tests/ir/no-function-calls/PrintAST.ql new file mode 100644 index 000000000000..03321d9e4910 --- /dev/null +++ b/cpp/ql/test/library-tests/ir/no-function-calls/PrintAST.ql @@ -0,0 +1,11 @@ +/** + * @kind graph + */ + +private import cpp +private import semmle.code.cpp.PrintAST +private import PrintConfig + +private class PrintConfig extends PrintAstConfiguration { + override predicate shouldPrintDeclaration(Declaration decl) { shouldDumpDeclaration(decl) } +} diff --git a/cpp/ql/test/library-tests/ir/no-function-calls/PrintConfig.qll b/cpp/ql/test/library-tests/ir/no-function-calls/PrintConfig.qll new file mode 100644 index 000000000000..aa23cf423add --- /dev/null +++ b/cpp/ql/test/library-tests/ir/no-function-calls/PrintConfig.qll @@ -0,0 +1,24 @@ +private import cpp + +/** + * Holds if the specified location is in standard headers. + */ +predicate locationIsInStandardHeaders(Location loc) { + loc.getFile().getAbsolutePath().regexpMatch(".*/include/[^/]+") +} + +/** + * Holds if the AST or IR for the specified declaration should be printed in the test output. + * + * This predicate excludes declarations defined in standard headers. + */ +predicate shouldDumpDeclaration(Declaration decl) { + not locationIsInStandardHeaders(decl.getLocation()) and + ( + decl instanceof Function + or + decl.(GlobalOrNamespaceVariable).hasInitializer() + or + decl.(StaticLocalVariable).hasInitializer() + ) +} diff --git a/cpp/ql/test/library-tests/ir/no-function-calls/aliased_ir.expected b/cpp/ql/test/library-tests/ir/no-function-calls/aliased_ir.expected new file mode 100644 index 000000000000..6b1c7a76a62b --- /dev/null +++ b/cpp/ql/test/library-tests/ir/no-function-calls/aliased_ir.expected @@ -0,0 +1,38 @@ +test.cpp: +# 5| void foo(int*) +# 5| Block 0 +# 5| v5_1(void) = EnterFunction : +# 5| m5_2(unknown) = AliasedDefinition : +# 5| m5_3(unknown) = InitializeNonLocal : +# 5| m5_4(unknown) = Chi : total:m5_2, partial:m5_3 +# 5| r5_5(glval) = VariableAddress[x] : +# 5| m5_6(int *) = InitializeParameter[x] : &:r5_5 +# 5| r5_7(int *) = Load[x] : &:r5_5, m5_6 +# 5| m5_8(unknown) = InitializeIndirection[x] : &:r5_7 +# 5| m5_9(unknown) = Chi : total:m5_4, partial:m5_8 +# 6| r6_1(glval) = FunctionAddress[operator delete] : +# 6| r6_2(glval) = VariableAddress[x] : +# 6| r6_3(int *) = Load[x] : &:r6_2, m5_6 +# 6| v6_4(void) = Call[operator delete] : func:r6_1, 0:r6_3 +# 6| m6_5(unknown) = ^CallSideEffect : ~m5_9 +# 6| m6_6(unknown) = Chi : total:m5_9, partial:m6_5 +# 7| v7_1(void) = NoOp : +# 5| v5_10(void) = ReturnIndirection[x] : &:r5_7, ~m6_6 +# 5| v5_11(void) = ReturnVoid : +# 5| v5_12(void) = AliasedUse : ~m6_6 +# 5| v5_13(void) = ExitFunction : + +# 11| void jazz() +# 11| Block 0 +# 11| v11_1(void) = EnterFunction : +# 11| m11_2(unknown) = AliasedDefinition : +# 11| m11_3(unknown) = InitializeNonLocal : +# 11| m11_4(unknown) = Chi : total:m11_2, partial:m11_3 +# 12| r12_1(glval) = FunctionAddress[bar] : +# 12| v12_2(void) = Call[bar] : func:r12_1 +# 12| m12_3(unknown) = ^CallSideEffect : ~m11_4 +# 12| m12_4(unknown) = Chi : total:m11_4, partial:m12_3 +# 13| v13_1(void) = NoOp : +# 11| v11_5(void) = ReturnVoid : +# 11| v11_6(void) = AliasedUse : ~m12_4 +# 11| v11_7(void) = ExitFunction : diff --git a/cpp/ql/test/library-tests/ir/no-function-calls/aliased_ir.ql b/cpp/ql/test/library-tests/ir/no-function-calls/aliased_ir.ql new file mode 100644 index 000000000000..0488fd09dbed --- /dev/null +++ b/cpp/ql/test/library-tests/ir/no-function-calls/aliased_ir.ql @@ -0,0 +1,11 @@ +/** + * @kind graph + */ + +private import cpp +private import semmle.code.cpp.ir.implementation.aliased_ssa.PrintIR +private import PrintConfig + +private class PrintConfig extends PrintIRConfiguration { + override predicate shouldPrintDeclaration(Declaration decl) { shouldDumpDeclaration(decl) } +} diff --git a/cpp/ql/test/library-tests/ir/no-function-calls/test.cpp b/cpp/ql/test/library-tests/ir/no-function-calls/test.cpp new file mode 100644 index 000000000000..a892ba41ba2b --- /dev/null +++ b/cpp/ql/test/library-tests/ir/no-function-calls/test.cpp @@ -0,0 +1,13 @@ +// Test for edge case, where we have a database without any function calls or +// where none of the function calls have any arguments, but where we do have +// a delete expression. + +void foo(int* x) { + delete x; +} + +void bar(); + +void jazz() { + bar(); +}