diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/ExpressionBuilder.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/ExpressionBuilder.kt index 1e80728150..c590d0af37 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/ExpressionBuilder.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/ExpressionBuilder.kt @@ -294,27 +294,6 @@ fun MetadataProvider.newCallExpression( return node } -/** - * Creates a new [CallExpression]. The [MetadataProvider] receiver will be used to fill different - * meta-data using [Node.applyMetadata]. Calling this extension function outside of Kotlin requires - * an appropriate [MetadataProvider], such as a [LanguageFrontend] as an additional prepended - * argument. - */ -@JvmOverloads -fun MetadataProvider.newConstructorCallExpression( - containingClass: String?, - code: String? = null, - rawNode: Any? = null -): ConstructorCallExpression { - val node = ConstructorCallExpression() - node.applyMetadata(this, EMPTY_NAME, rawNode, code, true) - - node.containingClass = containingClass - - log(node) - return node -} - /** * Creates a new [CallExpression]. The [MetadataProvider] receiver will be used to fill different * meta-data using [Node.applyMetadata]. Calling this extension function outside of Kotlin requires @@ -550,24 +529,6 @@ fun MetadataProvider.newInitializerListExpression( return node } -/** - * Creates a new [DesignatedInitializerExpression]. The [MetadataProvider] receiver will be used to - * fill different meta-data using [Node.applyMetadata]. Calling this extension function outside of - * Kotlin requires an appropriate [MetadataProvider], such as a [LanguageFrontend] as an additional - * prepended argument. - */ -@JvmOverloads -fun MetadataProvider.newDesignatedInitializerExpression( - code: String? = null, - rawNode: Any? = null -): DesignatedInitializerExpression { - val node = DesignatedInitializerExpression() - node.applyMetadata(this, EMPTY_NAME, rawNode, code, true) - - log(node) - return node -} - /** * Creates a new [TypeExpression]. The [MetadataProvider] receiver will be used to fill different * meta-data using [Node.applyMetadata]. Calling this extension function outside of Kotlin requires diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/StatementBuilder.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/StatementBuilder.kt index 18f4b65de9..82791c6799 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/StatementBuilder.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/StatementBuilder.kt @@ -97,17 +97,17 @@ fun MetadataProvider.newAssertStatement( } /** - * Creates a new [ASMDeclarationStatement]. The [MetadataProvider] receiver will be used to fill + * Creates a new [DistinctLanguageBlock]. The [MetadataProvider] receiver will be used to fill * different meta-data using [Node.applyMetadata]. Calling this extension function outside of Kotlin * requires an appropriate [MetadataProvider], such as a [LanguageFrontend] as an additional * prepended argument. */ @JvmOverloads -fun MetadataProvider.newASMDeclarationStatement( +fun MetadataProvider.newDistinctLanguageBlock( code: String? = null, rawNode: Any? = null -): ASMDeclarationStatement { - val node = ASMDeclarationStatement() +): DistinctLanguageBlock { + val node = DistinctLanguageBlock() node.applyMetadata(this, EMPTY_NAME, rawNode, code, true) log(node) diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/ASMDeclarationStatement.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/DistinctLanguageBlock.kt similarity index 81% rename from cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/ASMDeclarationStatement.kt rename to cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/DistinctLanguageBlock.kt index 4c75abff9c..7d32a57a4a 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/ASMDeclarationStatement.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/DistinctLanguageBlock.kt @@ -24,5 +24,11 @@ * */ package de.fraunhofer.aisec.cpg.graph.statements -// TODO Merge and/or refactor -class ASMDeclarationStatement : DeclarationStatement() + +import de.fraunhofer.aisec.cpg.graph.statements.expressions.Block + +/** + * A Block of code containing code in a different language that is not parsable with the same + * frontend as the enclosing language. + */ +class DistinctLanguageBlock : Block() diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/expressions/Block.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/expressions/Block.kt index 81d736fc22..6361e8441c 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/expressions/Block.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/expressions/Block.kt @@ -38,7 +38,7 @@ import org.neo4j.ogm.annotation.Relationship * A statement which contains a list of statements. A common example is a function body within a * [FunctionDeclaration]. */ -class Block : Expression(), StatementHolder { +open class Block : Expression(), StatementHolder { /** The list of statements. */ @Relationship(value = "STATEMENTS", direction = Relationship.Direction.OUTGOING) @AST diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/expressions/ConstructorCallExpression.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/expressions/ConstructorCallExpression.kt deleted file mode 100644 index eda95685eb..0000000000 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/expressions/ConstructorCallExpression.kt +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright (c) 2020, Fraunhofer AISEC. All rights reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * $$$$$$\ $$$$$$$\ $$$$$$\ - * $$ __$$\ $$ __$$\ $$ __$$\ - * $$ / \__|$$ | $$ |$$ / \__| - * $$ | $$$$$$$ |$$ |$$$$\ - * $$ | $$ ____/ $$ |\_$$ | - * $$ | $$\ $$ | $$ | $$ | - * \$$$$$ |$$ | \$$$$$ | - * \______/ \__| \______/ - * - */ -package de.fraunhofer.aisec.cpg.graph.statements.expressions - -import java.util.Objects -import org.apache.commons.lang3.builder.ToStringBuilder - -// TODO Merge and/or refactor -class ConstructorCallExpression : CallExpression() { - var containingClass: String? = null - - override fun toString(): String { - return ToStringBuilder(this, TO_STRING_STYLE) - .appendSuper(super.toString()) - .append("invokes", invokes) - .toString() - } - - override fun equals(other: Any?): Boolean { - if (this === other) return true - if (other !is ConstructorCallExpression) return false - return super.equals(other) && containingClass == other.containingClass - } - - override fun hashCode() = Objects.hash(super.hashCode(), containingClass) -} diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/expressions/DesignatedInitializerExpression.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/expressions/DesignatedInitializerExpression.kt deleted file mode 100644 index ecfe7450ad..0000000000 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/expressions/DesignatedInitializerExpression.kt +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Copyright (c) 2020, Fraunhofer AISEC. All rights reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * $$$$$$\ $$$$$$$\ $$$$$$\ - * $$ __$$\ $$ __$$\ $$ __$$\ - * $$ / \__|$$ | $$ |$$ / \__| - * $$ | $$$$$$$ |$$ |$$$$\ - * $$ | $$ ____/ $$ |\_$$ | - * $$ | $$\ $$ | $$ | $$ | - * \$$$$$ |$$ | \$$$$$ | - * \______/ \__| \______/ - * - */ -package de.fraunhofer.aisec.cpg.graph.statements.expressions - -import de.fraunhofer.aisec.cpg.graph.AST -import de.fraunhofer.aisec.cpg.graph.edge.PropertyEdge -import de.fraunhofer.aisec.cpg.graph.edge.PropertyEdgeDelegate -import java.util.Objects -import org.apache.commons.lang3.builder.ToStringBuilder -import org.neo4j.ogm.annotation.Relationship - -// TODO Merge and/or refactor -// TODO: Document this class! -class DesignatedInitializerExpression : Expression() { - @AST var rhs: Expression? = null - - @Relationship(value = "LHS", direction = Relationship.Direction.OUTGOING) - @AST - var lhsEdges: MutableList> = mutableListOf() - - var lhs: List by PropertyEdgeDelegate(DesignatedInitializerExpression::lhsEdges) - - override fun toString(): String { - return ToStringBuilder(this, TO_STRING_STYLE) - .appendSuper(super.toString()) - .append("lhr", lhsEdges) - .append("rhs", rhs) - .toString() - } - - override fun equals(other: Any?): Boolean { - if (this === other) return true - if (other !is DesignatedInitializerExpression) return false - return super.equals(other) && - rhs == other.rhs && - lhs == other.lhs && - lhsEdges == other.lhsEdges - } - - override fun hashCode() = Objects.hash(super.hashCode(), rhs, lhs) -} diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/SymbolResolver.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/SymbolResolver.kt index ec72514d6e..22fa15979c 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/SymbolResolver.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/SymbolResolver.kt @@ -442,7 +442,6 @@ open class SymbolResolver(ctx: TranslationContext) : ComponentPass(ctx) { when (node) { is MemberExpression -> handleMemberExpression(currClass, node) is Reference -> handleReference(currClass, node) - is ConstructorCallExpression -> handleConstructorCallExpression(node) is ConstructExpression -> handleConstructExpression(node) is CallExpression -> handleCallExpression(scopeManager.currentRecord, node) } @@ -741,23 +740,6 @@ open class SymbolResolver(ctx: TranslationContext) : ComponentPass(ctx) { } } - protected fun handleConstructorCallExpression( - constructorCallExpression: ConstructorCallExpression - ) { - constructorCallExpression.containingClass?.let { containingClass -> - val recordDeclaration = - constructorCallExpression.objectType(containingClass).recordDeclaration - val signature = constructorCallExpression.arguments.map { it.type } - if (recordDeclaration != null) { - val constructor = - getConstructorDeclarationForExplicitInvocation(signature, recordDeclaration) - val invokes = mutableListOf() - invokes.add(constructor) - constructorCallExpression.invokes = invokes - } - } - } - protected fun getPossibleContainingTypes(node: Node?): Set { val possibleTypes = mutableSetOf() if (node is MemberCallExpression) { diff --git a/cpg-language-cxx/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/cxx/ExpressionHandler.kt b/cpg-language-cxx/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/cxx/ExpressionHandler.kt index 2af62aaa41..f250625573 100644 --- a/cpg-language-cxx/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/cxx/ExpressionHandler.kt +++ b/cpg-language-cxx/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/cxx/ExpressionHandler.kt @@ -40,7 +40,10 @@ import org.eclipse.cdt.core.dom.ast.* import org.eclipse.cdt.core.dom.ast.IASTBinaryExpression.* import org.eclipse.cdt.core.dom.ast.IASTLiteralExpression.* import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTLambdaExpression +import org.eclipse.cdt.internal.core.dom.parser.c.CASTArrayDesignator +import org.eclipse.cdt.internal.core.dom.parser.c.CASTArrayRangeDesignator import org.eclipse.cdt.internal.core.dom.parser.c.CASTDesignatedInitializer +import org.eclipse.cdt.internal.core.dom.parser.c.CASTFieldDesignator import org.eclipse.cdt.internal.core.dom.parser.cpp.* import org.eclipse.cdt.internal.core.model.ASTStringUtil @@ -556,102 +559,127 @@ class ExpressionHandler(lang: CXXLanguageFrontend) : } } - private fun handleCXXDesignatedInitializer( - ctx: CPPASTDesignatedInitializer - ): DesignatedInitializerExpression { + private fun handleCXXDesignatedInitializer(ctx: CPPASTDesignatedInitializer): Expression { val rhs = handle(ctx.operand) - val lhs = ArrayList() - if (ctx.designators.isEmpty()) { + + // We need to check the first designator first + val des = ctx.designators.firstOrNull() + if (des == null) { Util.errorWithFileLocation(frontend, ctx, log, "no designator found") - } else { - for (des in ctx.designators) { - var oneLhs: Expression? = null - when (des) { - is CPPASTArrayDesignator -> { - oneLhs = handle(des.subscriptExpression) - } - is CPPASTFieldDesignator -> { - oneLhs = newReference(des.name.toString(), unknownType(), rawNode = des) - } - is CPPASTArrayRangeDesignator -> { - oneLhs = - newRangeExpression( - handle(des.rangeFloor), - handle(des.rangeCeiling), - des.getRawSignature() - ) - oneLhs.operatorCode = "..." - } - else -> { - Util.errorWithFileLocation( - frontend, - ctx, - log, - "Unknown designated lhs {}", - des.javaClass.toGenericString() - ) + return newProblemExpression("no designator found") + } + + // We need to start with our target (which we need to find in a hacky way) as + // first ref + val baseName = + (((ctx.parent as? IASTInitializerList)?.parent as? IASTInitializer)?.parent + as? IASTDeclarator) + ?.name + .toString() + var ref = newReference(baseName) + + val lhs = + when (des) { + is CPPASTArrayDesignator -> { + val sub = newSubscriptExpression() + sub.arrayExpression = ref + handle(des.subscriptExpression)?.let { sub.subscriptExpression = it } + sub + } + is CPPASTFieldDesignator -> { + // Then we loop through all designators and chain them. Only field designators + // can be chained in this way + for (field in + ctx.designators.toList().filterIsInstance()) { + // the old ref is our new base + ref = newMemberExpression(field.name.toString(), ref, rawNode = field) } + ref } - if (oneLhs != null) { - lhs.add(oneLhs) + else -> { + Util.errorWithFileLocation( + frontend, + ctx, + log, + "Unknown designated lhs {}", + des.javaClass.toGenericString() + ) + null } } - } - - val die = newDesignatedInitializerExpression(rawNode = ctx) - die.lhs = lhs - die.rhs = rhs - return die + return newAssignExpression( + lhs = listOfNotNull(lhs), + rhs = listOfNotNull(rhs), + rawNode = ctx + ) } - private fun handleCDesignatedInitializer( - ctx: CASTDesignatedInitializer - ): DesignatedInitializerExpression { + private fun handleCDesignatedInitializer(ctx: CASTDesignatedInitializer): Expression { val rhs = handle(ctx.operand) - val lhs = ArrayList() - if (ctx.designators.isEmpty()) { + + // We need to check the first designator first + val des = ctx.designators.firstOrNull() + if (des == null) { Util.errorWithFileLocation(frontend, ctx, log, "no designator found") - } else { - for (des in ctx.designators) { - var oneLhs: Expression? = null - when (des) { - is CPPASTArrayDesignator -> { - oneLhs = handle(des.subscriptExpression) - } - is CPPASTFieldDesignator -> { - oneLhs = newReference(des.name.toString(), unknownType(), rawNode = des) - } - is CPPASTArrayRangeDesignator -> { - oneLhs = - newRangeExpression( - handle(des.rangeFloor), - handle(des.rangeCeiling), - des.getRawSignature() - ) - oneLhs.operatorCode = "..." - } - else -> { - Util.errorWithFileLocation( - frontend, - ctx, - log, - "Unknown designated lhs {}", - des.javaClass.toGenericString() - ) + return newProblemExpression("no designator found") + } + + // We need to start with our target (which we need to find in a hacky way) as + // first ref + val baseName = + (((ctx.parent as? IASTInitializerList)?.parent as? IASTInitializer)?.parent + as? IASTDeclarator) + ?.name + .toString() + var ref = newReference(baseName) + + val lhs = + when (des) { + is CASTArrayDesignator -> { + val sub = newSubscriptExpression(rawNode = des) + sub.arrayExpression = ref + handle(des.subscriptExpression)?.let { sub.subscriptExpression = it } + sub + } + is CASTArrayRangeDesignator -> { + val sub = newSubscriptExpression(rawNode = des) + sub.arrayExpression = ref + + val range = newRangeExpression(rawNode = des) + des.rangeFloor?.let { range.floor = handle(it) } + des.rangeCeiling?.let { range.ceiling = handle(it) } + range.operatorCode = "..." + sub.subscriptExpression = range + sub + } + is CASTFieldDesignator -> { + // Then we loop through all designators and chain them. Only field designators + // can be chained in this way + for (field in + ctx.designators.toList().filterIsInstance()) { + // the old ref is our new base + ref = newMemberExpression(field.name.toString(), ref, rawNode = field) } + ref } - if (oneLhs != null) { - lhs.add(oneLhs) + else -> { + Util.errorWithFileLocation( + frontend, + ctx, + log, + "Unknown designated lhs {}", + des.javaClass.toGenericString() + ) + null } } - } - - val die = newDesignatedInitializerExpression(rawNode = ctx) - die.lhs = lhs - die.rhs = rhs - return die + return newAssignExpression( + lhs = listOfNotNull(lhs), + rhs = listOfNotNull(rhs), + rawNode = ctx + ) } private fun handleIntegerLiteral(ctx: IASTLiteralExpression): Expression { diff --git a/cpg-language-cxx/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/cxx/StatementHandler.kt b/cpg-language-cxx/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/cxx/StatementHandler.kt index b98a0c3d9f..3c80c49f10 100644 --- a/cpg-language-cxx/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/cxx/StatementHandler.kt +++ b/cpg-language-cxx/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/cxx/StatementHandler.kt @@ -290,9 +290,11 @@ class StatementHandler(lang: CXXLanguageFrontend) : return expression } - private fun handleDeclarationStatement(ctx: IASTDeclarationStatement): DeclarationStatement { + private fun handleDeclarationStatement(ctx: IASTDeclarationStatement): Statement { return if (ctx.declaration is IASTASMDeclaration) { - newASMDeclarationStatement(rawNode = ctx) + // TODO: Specify the contained language through a language node and find a way to run a + // frontend for sub-block if available + newDistinctLanguageBlock(rawNode = ctx) } else { val declarationStatement = newDeclarationStatement(rawNode = ctx) val declaration = frontend.declarationHandler.handle(ctx.declaration) diff --git a/cpg-language-cxx/src/test/kotlin/de/fraunhofer/aisec/cpg/frontends/cxx/CXXLanguageFrontendTest.kt b/cpg-language-cxx/src/test/kotlin/de/fraunhofer/aisec/cpg/frontends/cxx/CXXLanguageFrontendTest.kt index 693673a7c2..2f9b001e77 100644 --- a/cpg-language-cxx/src/test/kotlin/de/fraunhofer/aisec/cpg/frontends/cxx/CXXLanguageFrontendTest.kt +++ b/cpg-language-cxx/src/test/kotlin/de/fraunhofer/aisec/cpg/frontends/cxx/CXXLanguageFrontendTest.kt @@ -1048,86 +1048,106 @@ internal class CXXLanguageFrontendTest : BaseTest() { @Test @Throws(Exception::class) - fun testDesignatedInitializer() { - val file = File("src/test/resources/components/designatedInitializer.cpp") + fun testCDesignatedInitializer() { + val file = File("src/test/resources/c/designated.c") + val tu = + analyzeAndGetFirstTU(listOf(file), file.parentFile.toPath(), true) { + it.registerLanguage() + } + + val foo3 = tu.variables["foo3"] + assertNotNull(foo3) + + val init = foo3.initializer + assertIs(init) + + val assign = init.initializers.firstOrNull() + assertIs(assign) + + val lhs = assign.lhs(0) + assertNotNull(lhs) + } + + @Test + @Throws(Exception::class) + fun testCPPDesignatedInitializer() { + val file = File("src/test/resources/cxx/designated.cpp") val declaration = analyzeAndGetFirstTU(listOf(file), file.parentFile.toPath(), true) { it.registerLanguage() } - // should be four method nodes - assertEquals(2, declaration.declarations.size) - - val method = declaration.getDeclarationAs(1, FunctionDeclaration::class.java) - assertEquals("main()int", method!!.signature) + val method = declaration.functions["main"] + assertNotNull(method) + assertEquals("main()int", method.signature) assertTrue(method.body is Block) val statements = (method.body as Block).statements - assertEquals(4, statements.size) + assertEquals(5, statements.size) assertTrue(statements[0] is DeclarationStatement) assertTrue(statements[1] is DeclarationStatement) assertTrue(statements[2] is DeclarationStatement) - assertTrue(statements[3] is ReturnStatement) + assertTrue(statements[3] is DeclarationStatement) + assertTrue(statements[4] is ReturnStatement) var initializer = ((statements[0] as DeclarationStatement).singleDeclaration as VariableDeclaration) .initializer assertTrue(initializer is InitializerListExpression) assertEquals(3, initializer.initializers.size) - assertTrue(initializer.initializers[0] is DesignatedInitializerExpression) - assertTrue(initializer.initializers[1] is DesignatedInitializerExpression) - assertTrue(initializer.initializers[2] is DesignatedInitializerExpression) + assertTrue(initializer.initializers[0] is AssignExpression) + assertTrue(initializer.initializers[1] is AssignExpression) + assertTrue(initializer.initializers[2] is AssignExpression) - var die = initializer.initializers[0] as DesignatedInitializerExpression + var die = initializer.initializers[0] as AssignExpression assertTrue(die.lhs[0] is Reference) - assertTrue(die.rhs is Literal<*>) + assertTrue(die.rhs[0] is Literal<*>) assertLocalName("y", die.lhs[0]) - assertEquals(0, (die.rhs as Literal<*>).value) + assertEquals(0, (die.rhs[0] as Literal<*>).value) - die = initializer.initializers[1] as DesignatedInitializerExpression + die = initializer.initializers[1] as AssignExpression assertTrue(die.lhs[0] is Reference) - assertTrue(die.rhs is Literal<*>) + assertTrue(die.rhs[0] is Literal<*>) assertLocalName("z", die.lhs[0]) - assertEquals(1, (die.rhs as Literal<*>).value) + assertEquals(1, (die.rhs[0] as Literal<*>).value) - die = initializer.initializers[2] as DesignatedInitializerExpression + die = initializer.initializers[2] as AssignExpression assertTrue(die.lhs[0] is Reference) - assertTrue(die.rhs is Literal<*>) + assertTrue(die.rhs[0] is Literal<*>) assertLocalName("x", die.lhs[0]) - assertEquals(2, (die.rhs as Literal<*>).value) + assertEquals(2, (die.rhs[0] as Literal<*>).value) initializer = ((statements[1] as DeclarationStatement).singleDeclaration as VariableDeclaration) .initializer assertTrue(initializer is InitializerListExpression) assertEquals(1, initializer.initializers.size) - assertTrue(initializer.initializers[0] is DesignatedInitializerExpression) + assertTrue(initializer.initializers[0] is AssignExpression) - die = initializer.initializers[0] as DesignatedInitializerExpression + die = initializer.initializers[0] as AssignExpression assertTrue(die.lhs[0] is Reference) - assertTrue(die.rhs is Literal<*>) + assertTrue(die.rhs[0] is Literal<*>) assertLocalName("x", die.lhs[0]) - assertEquals(20, (die.rhs as Literal<*>).value) + assertEquals(20, (die.rhs[0] as Literal<*>).value) initializer = - ((statements[2] as DeclarationStatement).singleDeclaration as VariableDeclaration) + ((statements[3] as DeclarationStatement).singleDeclaration as VariableDeclaration) .initializer assertTrue(initializer is InitializerListExpression) assertEquals(2, initializer.initializers.size) - assertTrue(initializer.initializers[0] is DesignatedInitializerExpression) - assertTrue(initializer.initializers[1] is DesignatedInitializerExpression) - - die = initializer.initializers[0] as DesignatedInitializerExpression - assertTrue(die.lhs[0] is Literal<*>) - assertTrue(die.rhs is Literal<*>) - assertEquals(3, (die.lhs[0] as Literal<*>).value) - assertEquals(1, (die.rhs as Literal<*>).value) - - die = initializer.initializers[1] as DesignatedInitializerExpression - assertTrue(die.lhs[0] is Literal<*>) - assertTrue(die.rhs is Literal<*>) - assertEquals(5, (die.lhs[0] as Literal<*>).value) - assertEquals(2, (die.rhs as Literal<*>).value) + assertTrue(initializer.initializers[0] is AssignExpression) + assertTrue(initializer.initializers[1] is AssignExpression) + + die = initializer.initializers[0] as AssignExpression + assertLiteralValue(3, (die.lhs[0] as SubscriptExpression).subscriptExpression) + assertLiteralValue(1, die.rhs[0]) + + die = initializer.initializers[1] as AssignExpression + assertLiteralValue(5, (die.lhs[0] as SubscriptExpression).subscriptExpression) + assertLiteralValue(2, die.rhs[0]) + + val o = declaration.variables["o"] + assertNotNull(o) } @Test diff --git a/cpg-language-cxx/src/test/resources/c/designated.c b/cpg-language-cxx/src/test/resources/c/designated.c new file mode 100644 index 0000000000..49e0797ea0 --- /dev/null +++ b/cpg-language-cxx/src/test/resources/c/designated.c @@ -0,0 +1,23 @@ +struct Point +{ + int x, y, z; +}; + +struct Outer { + struct Point p; +}; + +int main() +{ + // Examples of initialization using + // designated initialization + struct Point p1 = {.y = 0, .z = 1, .x = 2}; + struct Point p2 = {.x = 20}; + struct Outer o = {.p.x = 10}; + int foo2[10] = { [3] = 1, [5] = 2 }; + + // This only works in C (!!) + int foo3[10] = { [0 ... 9] = 2 }; + + return 0; +} \ No newline at end of file diff --git a/cpg-language-cxx/src/test/resources/components/designatedInitializer.cpp b/cpg-language-cxx/src/test/resources/cxx/designated.cpp similarity index 84% rename from cpg-language-cxx/src/test/resources/components/designatedInitializer.cpp rename to cpg-language-cxx/src/test/resources/cxx/designated.cpp index 6bd971e746..f57c0a5717 100644 --- a/cpg-language-cxx/src/test/resources/components/designatedInitializer.cpp +++ b/cpg-language-cxx/src/test/resources/cxx/designated.cpp @@ -10,12 +10,17 @@ struct Point int x, y, z; }; +struct Outer { + struct Point p; +}; + int main() { // Examples of initialization using // designated initialization struct Point p1 = {.y = 0, .z = 1, .x = 2}; struct Point p2 = {.x = 20}; + struct Outer o = {.p.x = 10}; int foo2[10] = { [3] = 1, [5] = 2 }; return 0; diff --git a/cpg-language-java/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/java/StatementHandler.kt b/cpg-language-java/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/java/StatementHandler.kt index 70c79305ad..b14bbf7b32 100644 --- a/cpg-language-java/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/java/StatementHandler.kt +++ b/cpg-language-java/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/java/StatementHandler.kt @@ -51,10 +51,7 @@ import de.fraunhofer.aisec.cpg.graph.statements.SwitchStatement import de.fraunhofer.aisec.cpg.graph.statements.SynchronizedStatement import de.fraunhofer.aisec.cpg.graph.statements.TryStatement import de.fraunhofer.aisec.cpg.graph.statements.WhileStatement -import de.fraunhofer.aisec.cpg.graph.statements.expressions.Block -import de.fraunhofer.aisec.cpg.graph.statements.expressions.ConstructorCallExpression -import de.fraunhofer.aisec.cpg.graph.statements.expressions.Literal -import de.fraunhofer.aisec.cpg.graph.statements.expressions.ProblemExpression +import de.fraunhofer.aisec.cpg.graph.statements.expressions.* import de.fraunhofer.aisec.cpg.graph.types.Type import de.fraunhofer.aisec.cpg.sarif.PhysicalLocation import de.fraunhofer.aisec.cpg.sarif.Region @@ -517,7 +514,7 @@ class StatementHandler(lang: JavaLanguageFrontend?) : return switchStatement } - private fun handleExplicitConstructorInvocation(stmt: Statement): ConstructorCallExpression { + private fun handleExplicitConstructorInvocation(stmt: Statement): ConstructExpression { val explicitConstructorInvocationStmt = stmt.asExplicitConstructorInvocationStmt() var containingClass = "" val currentRecord = frontend.scopeManager.currentRecord @@ -528,21 +525,26 @@ class StatementHandler(lang: JavaLanguageFrontend?) : } else { containingClass = currentRecord.name.toString() } - val node = - this.newConstructorCallExpression( - containingClass, - explicitConstructorInvocationStmt.toString() - ) + + val name = containingClass + val node = this.newConstructExpression(name, rawNode = null) + node.type = unknownType() + + // Create a reference either to "this" + if (explicitConstructorInvocationStmt.isThis) { + frontend.scopeManager.currentRecord?.toType()?.let { node.type = it } + node.callee = this.newReference(name) + } else { + // or to our direct (first) super type + frontend.scopeManager.currentRecord?.superTypes?.firstOrNull()?.let { + node.type = it + node.callee = this.newReference(it.name) + } + } val arguments = explicitConstructorInvocationStmt.arguments - .stream() - .map { ctx: Expression -> frontend.expressionHandler.handle(ctx) } - .map { obj: de.fraunhofer.aisec.cpg.graph.statements.Statement? -> - de.fraunhofer.aisec.cpg.graph.statements.expressions.Expression::class - .java - .cast(obj) - } - .collect(Collectors.toList()) + .map(frontend.expressionHandler::handle) + .filterIsInstance() node.arguments = arguments return node } diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index 7f93135c49..d64cd49177 100644 Binary files a/gradle/wrapper/gradle-wrapper.jar and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 3fa8f862f7..1af9e0930b 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.4-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.5-bin.zip networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME