diff --git a/chapi-ast-c/src/main/antlr/C.g4 b/chapi-ast-c/src/main/antlr/C.g4 index 843c69c8..675d09d7 100644 --- a/chapi-ast-c/src/main/antlr/C.g4 +++ b/chapi-ast-c/src/main/antlr/C.g4 @@ -69,15 +69,17 @@ genericAssociation ; postfixExpression - : (primaryExpression | '__extension__'? '(' typeName ')' '{' initializerList ','? '}') ( - '[' expression ']' - | '(' argumentExpressionList? ')' - | ('.' | '->') Identifier - | '++' - | '--' - )* + : (primaryExpression | extensionExpression) (postixCall | '++' | '--')* ; +extensionExpression : '__extension__'? '(' typeName ')' '{' initializerList ','? '}' ; + +postixCall + :'[' expression ']' #arrayAccessPostfixExpression + | '(' argumentExpressionList? ')' #functionCallPostfixExpression + | ('.' | '->') Identifier #memberAccessPostfixExpression + ; + argumentExpressionList : assignmentExpression (',' assignmentExpression)* ; diff --git a/chapi-ast-c/src/main/kotlin/chapi/ast/cast/CFullIdentListener.kt b/chapi-ast-c/src/main/kotlin/chapi/ast/cast/CFullIdentListener.kt index a71d8737..4e01a4ab 100644 --- a/chapi-ast-c/src/main/kotlin/chapi/ast/cast/CFullIdentListener.kt +++ b/chapi-ast-c/src/main/kotlin/chapi/ast/cast/CFullIdentListener.kt @@ -189,6 +189,47 @@ open class CFullIdentListener(fileName: String) : CAstBaseListener() { } + override fun enterPostfixExpression(ctx: CParser.PostfixExpressionContext?) { + val call = ctx?.postixCall() ?: return + val functionName = ctx.primaryExpression()?.Identifier()?.text ?: return + + val children = call.firstOrNull()?.children ?: return + // function call children should be '(', some parameters?, ')', so the size should be at least 2 + if (children.size < 2) return + + val codeCall = CodeCall( + FunctionName = functionName, + Parameters = buildParameters(call), + Position = buildPosition(ctx) + ) + + currentFunction.FunctionCalls += codeCall + } + + private fun buildParameters(call: List): List { + return call.mapNotNull { callContext -> + when(callContext) { + is CParser.ArrayAccessPostfixExpressionContext -> { + null + } + is CParser.FunctionCallPostfixExpressionContext -> { + callContext.argumentExpressionList()?.assignmentExpression()?.map { + CodeProperty( + TypeType = it.text, + TypeValue = it.text + ) + }?.toList() ?: listOf() + } + + is CParser.MemberAccessPostfixExpressionContext -> { + null + } + + else -> null + } + }.flatten() + } + fun getNodeInfo(): CodeContainer { if (defaultDataStruct.Functions.isNotEmpty()) { codeContainer.DataStructures += defaultDataStruct diff --git a/chapi-ast-c/src/test/kotlin/chapi/ast/cast/CFullIdentListenerTest.kt b/chapi-ast-c/src/test/kotlin/chapi/ast/cast/CFullIdentListenerTest.kt index 7dab02be..41138331 100644 --- a/chapi-ast-c/src/test/kotlin/chapi/ast/cast/CFullIdentListenerTest.kt +++ b/chapi-ast-c/src/test/kotlin/chapi/ast/cast/CFullIdentListenerTest.kt @@ -196,4 +196,38 @@ typedef struct { assertEquals(elementDs.Fields[1].TypeType, "struct element") assertEquals(elementDs.Fields[1].TypeValue, "") } + + @Test + fun shouldIdentifyFirstFunctionCall() { + val code = """ + void aX(void); + int a1(int param1); + int a2(int param1, param2); + void a3(); + void a3(void); + + int f(int arg1, char arg2) + { + a1(arg1); + a2(arg1, arg2); + a3(); + } + """.trimIndent() + + val codeFile = CAnalyser().analysis(code, "helloworld.c") + assertEquals(codeFile.DataStructures.size, 1) + assertEquals(codeFile.DataStructures[0].Functions.size, 1) + val functionCalls = codeFile.DataStructures[0].Functions[0].FunctionCalls + assertEquals(functionCalls.size, 3) + assertEquals(functionCalls[0].FunctionName, "a1") + assertEquals(functionCalls[0].Parameters[0].TypeValue, "arg1") + + assertEquals(functionCalls[1].FunctionName, "a2") + assertEquals(functionCalls[1].Parameters[0].TypeValue, "arg1") + assertEquals(functionCalls[1].Parameters[1].TypeValue, "arg2") + + assertEquals(functionCalls[2].FunctionName, "a3") + assertEquals(functionCalls[2].Parameters.size, 0) + } } +