Skip to content

Commit

Permalink
Improved guessCastExpressions in C++ frontend
Browse files Browse the repository at this point in the history
There were more ambiguities when guesssing cast expressions, so I added them.
Fixes #1347
  • Loading branch information
oxisto committed Nov 20, 2023
1 parent 0770194 commit 81e5adc
Show file tree
Hide file tree
Showing 3 changed files with 67 additions and 13 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -345,9 +345,10 @@ class ExpressionHandler(lang: CXXLanguageFrontend) :
val typeName = (ctx.operand as IASTIdExpression).name.toString()
if (frontend.typeManager.typeExists(typeName)) {
val cast = newCastExpression(frontend.codeOf(ctx))
cast.setCastOperator(0)
cast.castType = frontend.typeOf((ctx.operand as IASTIdExpression).name)
// The expression member can only be filled by the parent call
// (handleFunctionCallExpression)
// (handleFunctionCallExpression and handleBinaryExpression)
cast.location = frontend.locationOf(ctx)
return cast
}
Expand Down Expand Up @@ -429,7 +430,8 @@ class ExpressionHandler(lang: CXXLanguageFrontend) :
)
.forEach { callExpression.addTemplateParameter(it) }
}
reference is CastExpression -> {
reference is CastExpression &&
frontend.config.inferenceConfiguration.guessCastExpressions -> {
// this really is a cast expression in disguise
reference.expression =
ctx.arguments.firstOrNull()?.let { handle(it) }
Expand Down Expand Up @@ -501,6 +503,22 @@ class ExpressionHandler(lang: CXXLanguageFrontend) :
handle(ctx.initOperand2)
}
?: newProblemExpression("could not parse rhs")

if (lhs is CastExpression && frontend.config.inferenceConfiguration.guessCastExpressions) {
// this really is a combination of a cast and a unary operator
val op =
newUnaryOperator(
operatorCode,
postfix = true,
prefix = false,
code = ctx.rawSignature
)
op.input = rhs
op.location = frontend.locationOf(ctx.operand2)
lhs.expression = op
return lhs
}

binaryOperator.lhs = lhs
binaryOperator.rhs = rhs

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1320,21 +1320,43 @@ internal class CXXLanguageFrontendTest : BaseTest() {
it.inferenceConfiguration(builder().guessCastExpressions(true).build())
it.registerLanguage<CPPLanguage>()
}
val main =
tu.getDeclarationsByName("main", FunctionDeclaration::class.java).iterator().next()
val main = tu.functions["main"]
assertNotNull(main)

val declStatement = main.getBodyStatementAs(0, DeclarationStatement::class.java)
assertNotNull(declStatement)
val count = tu.variables["count"]
assertNotNull(count)

val decl = declStatement.singleDeclaration as VariableDeclaration
assertNotNull(decl)
var cast = count.initializer
assertIs<CastExpression>(cast)
assertLocalName("size_t", cast.castType)
assertLiteralValue(42, cast.expression)

val initializer = decl.initializer
assertNotNull(initializer)
assertTrue(initializer is CastExpression)
assertLocalName("size_t", initializer.castType)
assertLiteralValue(42, initializer.expression)
val addr = tu.variables["addr"]
assertNotNull(addr)

cast = addr.initializer
assertIs<CastExpression>(cast)
assertLocalName("int64_t", cast.castType)

val unary = cast.expression
assertIs<UnaryOperator>(unary)

val refCount = unary.input
assertIs<Reference>(refCount)
assertRefersTo(refCount, count)

var paths = addr.followPrevDFGEdgesUntilHit { it == refCount }
assertTrue(paths.fulfilled.isNotEmpty())
assertTrue(paths.failed.isEmpty())

val refKey = tu.refs["key"]
assertNotNull(refKey)

val assign = tu.assignments.firstOrNull { it.value is UnaryOperator }
assertNotNull(assign)
paths = assign.value.followPrevDFGEdgesUntilHit { it == refKey }
assertTrue(paths.fulfilled.isNotEmpty())
assertTrue(paths.failed.isEmpty())
}

@Test
Expand Down
14 changes: 14 additions & 0 deletions cpg-language-cxx/src/test/resources/cxx/parenthesis.cpp
Original file line number Diff line number Diff line change
@@ -1,4 +1,18 @@
// These headers are just here so that we could compile it, if we want it,
// to check for errors with clang. We will not parse them.
#include <cstddef>
#include <cstdint>

int main() {
// this cast could be mistaken for a call expression
size_t count = (size_t)(42);

// this cast could be mistaken for a binary operation
int64_t addr = (int64_t) &count;

// finally, a more complex example of unary operators and casts
char* outptr, key;
*(int64_t *)outptr = *(int64_t *)&key;

return 0;
}

0 comments on commit 81e5adc

Please sign in to comment.