Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Handle delete statement in python frontend #1723

Merged
merged 10 commits into from
Sep 25, 2024
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
*/
package de.fraunhofer.aisec.cpg.graph.statements.expressions

import de.fraunhofer.aisec.cpg.graph.edges.ast.astEdgesOf
import de.fraunhofer.aisec.cpg.graph.edges.ast.astOptionalEdgeOf
import de.fraunhofer.aisec.cpg.graph.edges.unwrapping
import java.util.Objects
Expand All @@ -34,11 +35,14 @@ class DeleteExpression : Expression() {
@Relationship("OPERAND") var operandEdge = astOptionalEdgeOf<Expression>()
var operand by unwrapping(DeleteExpression::operandEdge)

@Relationship(value = "TARGETS") var targetEdges = astEdgesOf<Expression>()
var targets by unwrapping(DeleteExpression::targetEdges)
oxisto marked this conversation as resolved.
Show resolved Hide resolved

override fun equals(other: Any?): Boolean {
if (this === other) return true
if (other !is DeleteExpression) return false
return super.equals(other) && operand == other.operand
return super.equals(other) && operand == other.operand && targets == other.targets
}

override fun hashCode() = Objects.hash(super.hashCode(), operand)
override fun hashCode() = Objects.hash(super.hashCode(), operand, targets)
}
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ import de.fraunhofer.aisec.cpg.graph.statements.AssertStatement
import de.fraunhofer.aisec.cpg.graph.statements.DeclarationStatement
import de.fraunhofer.aisec.cpg.graph.statements.Statement
import de.fraunhofer.aisec.cpg.graph.statements.expressions.Block
import de.fraunhofer.aisec.cpg.graph.statements.expressions.DeleteExpression
import de.fraunhofer.aisec.cpg.graph.statements.expressions.Expression
import de.fraunhofer.aisec.cpg.graph.statements.expressions.MemberExpression
import de.fraunhofer.aisec.cpg.graph.statements.expressions.ProblemExpression
Expand Down Expand Up @@ -67,7 +68,7 @@ class StatementHandler(frontend: PythonLanguageFrontend) :
is Python.AST.Break -> newBreakStatement(rawNode = node)
is Python.AST.Continue -> newContinueStatement(rawNode = node)
is Python.AST.Assert -> handleAssert(node)
is Python.AST.Delete,
is Python.AST.Delete -> handleDelete(node)
is Python.AST.Global,
is Python.AST.Match,
is Python.AST.Nonlocal,
Expand All @@ -83,6 +84,26 @@ class StatementHandler(frontend: PythonLanguageFrontend) :
}
}

/**
* Translates a Python [`Delete`](https://docs.python.org/3/library/ast.html#ast.Delete) into a
* [DeleteExpression].
*/
private fun handleDelete(node: Python.AST.Delete): DeleteExpression {
val delete = newDeleteExpression(rawNode = node)
node.targets.forEach { target ->
if (target is Python.AST.Subscript) {
delete.targets.add(frontend.expressionHandler.handle(target))
} else {
delete.additionalProblems +=
newProblemExpression(
problem = "Delete is only supported for Subscript targets.",
oxisto marked this conversation as resolved.
Show resolved Hide resolved
rawNode = target
)
}
}
return delete
}

/**
* Translates a Python (https://docs.python.org/3/library/ast.html#ast.Assert] into a
* [AssertStatement].
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,14 +29,17 @@ import de.fraunhofer.aisec.cpg.TranslationResult
import de.fraunhofer.aisec.cpg.frontends.python.PythonLanguage
import de.fraunhofer.aisec.cpg.graph.*
import de.fraunhofer.aisec.cpg.graph.statements.AssertStatement
import de.fraunhofer.aisec.cpg.graph.statements.expressions.DeleteExpression
import de.fraunhofer.aisec.cpg.graph.statements.expressions.Literal
import de.fraunhofer.aisec.cpg.graph.statements.expressions.SubscriptExpression
import de.fraunhofer.aisec.cpg.test.analyze
import de.fraunhofer.aisec.cpg.test.analyzeAndGetFirstTU
import de.fraunhofer.aisec.cpg.test.assertResolvedType
import java.nio.file.Path
import kotlin.test.Test
import kotlin.test.assertEquals
import kotlin.test.assertNotNull
import kotlin.test.assertTrue
import org.junit.jupiter.api.BeforeAll
import org.junit.jupiter.api.TestInstance

Expand Down Expand Up @@ -123,4 +126,34 @@ class StatementHandlerTest {
assertNotNull(message, "Assert statement should have a message")
assertEquals("Test message", message.value, "The assert message is incorrect")
}

@Test
fun testDeleteStatements() {
analyzeFile("delete.py")

val deleteExpressions = result.statements.filterIsInstance<DeleteExpression>()
assertEquals(4, deleteExpressions.size)

// Test for `del a`
val deleteStmt1 = deleteExpressions[0]
assertEquals(0, deleteStmt1.targets.size)
assertEquals(1, deleteStmt1.additionalProblems.size)

// Test for `del my_list[2]`
val deleteStmt2 = deleteExpressions[1]
assertEquals(1, deleteStmt2.targets.size)
assertTrue(deleteStmt2.targets.first() is SubscriptExpression)
assertTrue(deleteStmt2.additionalProblems.isEmpty())

// Test for `del my_dict['b']`
val deleteStmt3 = deleteExpressions[2]
assertEquals(1, deleteStmt3.targets.size)
assertTrue(deleteStmt3.targets.first() is SubscriptExpression)
assertTrue(deleteStmt3.additionalProblems.isEmpty())

// Test for `del obj.d`
val deleteStmt4 = deleteExpressions[3]
assertEquals(0, deleteStmt4.targets.size)
assertEquals(1, deleteStmt4.additionalProblems.size)
}
}
16 changes: 16 additions & 0 deletions cpg-language-python/src/test/resources/python/delete.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
a = 5
b = a
del a

my_list = [1, 2, 3]
del my_list[2]

my_dict = {'a': 1, 'b': 2}
del my_dict['b']

class MyClass:
def __init__(self):
self.d = 1

obj = MyClass()
del obj.d
Loading