Skip to content

Commit 18a1e39

Browse files
KuechAoxisto
andcommitted
Do not enter local scopes in python (#1982)
* Do not enter local scopes in python * Revert wronge change * Fixed testGlobal * Fixed testVarsAndFields * Fixed testNonLocal --------- Co-authored-by: Christian Banse <[email protected]>
1 parent 960ce1d commit 18a1e39

File tree

4 files changed

+28
-33
lines changed

4 files changed

+28
-33
lines changed

Diff for: cpg-language-python/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/python/StatementHandler.kt

+4-21
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ import de.fraunhofer.aisec.cpg.frontends.python.PythonLanguage.Companion.MODIFIE
3333
import de.fraunhofer.aisec.cpg.graph.*
3434
import de.fraunhofer.aisec.cpg.graph.Annotation
3535
import de.fraunhofer.aisec.cpg.graph.declarations.*
36-
import de.fraunhofer.aisec.cpg.graph.scopes.LocalScope
36+
import de.fraunhofer.aisec.cpg.graph.scopes.FunctionScope
3737
import de.fraunhofer.aisec.cpg.graph.scopes.NameScope
3838
import de.fraunhofer.aisec.cpg.graph.scopes.NamespaceScope
3939
import de.fraunhofer.aisec.cpg.graph.statements.*
@@ -891,11 +891,7 @@ class StatementHandler(frontend: PythonLanguageFrontend) :
891891
handleArguments(s.args, result, recordDeclaration)
892892

893893
if (s.body.isNotEmpty()) {
894-
// Make sure we open a new (block) scope for the function body. This is not a 1:1
895-
// mapping to python scopes, since python only has a "function scope", but in the CPG
896-
// the function scope only comprises the function arguments, and we need a block scope
897-
// to hold all local variables within the function body.
898-
result.body = makeBlock(s.body, parentNode = s, enterScope = true)
894+
result.body = makeBlock(s.body, parentNode = s)
899895
}
900896

901897
frontend.scopeManager.leaveScope(result)
@@ -927,11 +923,10 @@ class StatementHandler(frontend: PythonLanguageFrontend) :
927923
* into a [LookupScopeStatement].
928924
*/
929925
private fun handleNonLocal(global: Python.AST.Nonlocal): LookupScopeStatement {
930-
// We need to find the first outer function scope, or rather the block scope belonging to
931-
// the function
926+
// We need to find the first outer function scope
932927
var outerFunctionScope =
933928
frontend.scopeManager.firstScopeOrNull {
934-
it is LocalScope && it != frontend.scopeManager.currentScope
929+
it is FunctionScope && it != frontend.scopeManager.currentScope
935930
}
936931

937932
return newLookupScopeStatement(
@@ -1154,29 +1149,17 @@ class StatementHandler(frontend: PythonLanguageFrontend) :
11541149
* This function "wraps" a list of [Python.AST.BaseStmt] nodes into a [Block]. Since the list
11551150
* itself does not have a code/location, we need to employ [codeAndLocationFromChildren] on the
11561151
* [parentNode].
1157-
*
1158-
* Optionally, a new scope will be opened when [enterScope] is specified. This should be done
1159-
* VERY carefully, as Python has a very limited set of scopes and is most likely only to be used
1160-
* by [handleFunctionDef].
11611152
*/
11621153
private fun makeBlock(
11631154
stmts: List<Python.AST.BaseStmt>,
11641155
parentNode: Python.AST.WithLocation,
1165-
enterScope: Boolean = false,
11661156
): Block {
11671157
val result = newBlock()
1168-
if (enterScope) {
1169-
frontend.scopeManager.enterScope(result)
1170-
}
11711158

11721159
for (stmt in stmts) {
11731160
result.statements += handle(stmt)
11741161
}
11751162

1176-
if (enterScope) {
1177-
frontend.scopeManager.leaveScope(result)
1178-
}
1179-
11801163
// Try to retrieve the code and location from the parent node, if it is a base stmt
11811164
val ast = parentNode as? Python.AST.AST
11821165
if (ast != null) {

Diff for: cpg-language-python/src/test/kotlin/de/fraunhofer/aisec/cpg/frontends/python/PythonFrontendTest.kt

+17-3
Original file line numberDiff line numberDiff line change
@@ -742,13 +742,22 @@ class PythonFrontendTest : BaseTest() {
742742
assertNotNull(classFieldDeclaredInFunction)
743743
assertNull(classFieldNoInitializer.initializer)
744744

745-
val localClassFieldNoInitializer = methBar.variables["classFieldNoInitializer"]
745+
val localClassFieldNoInitializer =
746+
methBar.variables[
747+
{ it.name.localName == "classFieldNoInitializer" && it !is FieldDeclaration }]
746748
assertNotNull(localClassFieldNoInitializer)
747749

748-
val localClassFieldWithInit = methBar.variables["classFieldWithInit"]
750+
val localClassFieldWithInit =
751+
methBar.variables[
752+
{ it.name.localName == "classFieldWithInit" && it !is FieldDeclaration }]
749753
assertNotNull(localClassFieldNoInitializer)
750754

751-
val localClassFieldDeclaredInFunction = methBar.variables["classFieldDeclaredInFunction"]
755+
val localClassFieldDeclaredInFunction =
756+
methBar.variables[
757+
{
758+
it.name.localName == "classFieldDeclaredInFunction" &&
759+
it !is FieldDeclaration
760+
}]
752761
assertNotNull(localClassFieldNoInitializer)
753762

754763
// classFieldNoInitializer = classFieldWithInit
@@ -1763,5 +1772,10 @@ class PythonFrontendTest : BaseTest() {
17631772

17641773
// There is no field called "b" in the result.
17651774
assertNull(tu.fields["b"])
1775+
1776+
val foo = tu.functions["foo"]
1777+
assertNotNull(foo)
1778+
val refersTo = foo.refs("fooA").map { it.refersTo }
1779+
refersTo.forEach { refersTo -> assertIs<ParameterDeclaration>(refersTo) }
17661780
}
17671781
}

Diff for: cpg-language-python/src/test/kotlin/de/fraunhofer/aisec/cpg/frontends/python/statementHandler/StatementHandlerTest.kt

+2-8
Original file line numberDiff line numberDiff line change
@@ -251,16 +251,10 @@ class StatementHandlerTest : BaseTest() {
251251
var globalC = cVariables.firstOrNull { it.scope == pythonGlobalScope }
252252
assertNotNull(globalC)
253253

254-
var localC1 =
255-
cVariables.firstOrNull {
256-
it.scope?.astNode?.astParent?.name?.localName == "local_write"
257-
}
254+
var localC1 = cVariables.firstOrNull { it.scope?.astNode?.name?.localName == "local_write" }
258255
assertNotNull(localC1)
259256

260-
var localC2 =
261-
cVariables.firstOrNull {
262-
it.scope?.astNode?.astParent?.name?.localName == "error_write"
263-
}
257+
var localC2 = cVariables.firstOrNull { it.scope?.astNode?.name?.localName == "error_write" }
264258
assertNotNull(localC2)
265259

266260
// In global_write, all references should point to global c

Diff for: cpg-language-python/src/test/resources/python/variable_inference.py

+5-1
Original file line numberDiff line numberDiff line change
@@ -9,4 +9,8 @@ class SomeClass2:
99
def static_method(a):
1010
x = a
1111
b = x
12-
return b
12+
return b
13+
14+
def foo(fooA, b):
15+
fooA = bar(fooA)
16+
return fooA

0 commit comments

Comments
 (0)