From f121860819f08040aeff445f53cce8b1a74b045f Mon Sep 17 00:00:00 2001 From: Maksim Grebeniuk Date: Wed, 23 Oct 2024 17:32:37 +0200 Subject: [PATCH 1/3] SONARPY-2245 Fix resolving type for a name if its type is UnresolvedImportType --- .../v2/types/TrivialTypeInferenceVisitor.java | 2 +- .../python/semantic/v2/TypeInferenceV2Test.java | 17 +++++++++++++++++ 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/python-frontend/src/main/java/org/sonar/python/semantic/v2/types/TrivialTypeInferenceVisitor.java b/python-frontend/src/main/java/org/sonar/python/semantic/v2/types/TrivialTypeInferenceVisitor.java index 8a4027928f..bfb615d362 100644 --- a/python-frontend/src/main/java/org/sonar/python/semantic/v2/types/TrivialTypeInferenceVisitor.java +++ b/python-frontend/src/main/java/org/sonar/python/semantic/v2/types/TrivialTypeInferenceVisitor.java @@ -471,7 +471,7 @@ public void visitName(Name name) { .map(Expression.class::cast) .map(Expression::typeV2) // TODO: classes (SONARPY-1829) and functions should be propagated like other types - .filter(t -> (t instanceof ClassType) || (t instanceof FunctionType) || (t instanceof ModuleType)) + .filter(t -> (t instanceof ClassType) || (t instanceof FunctionType) || (t instanceof ModuleType) || (t instanceof UnknownType.UnresolvedImportType)) .ifPresent(type -> setTypeToName(name, type)); } diff --git a/python-frontend/src/test/java/org/sonar/python/semantic/v2/TypeInferenceV2Test.java b/python-frontend/src/test/java/org/sonar/python/semantic/v2/TypeInferenceV2Test.java index 6b1222d38e..998cb6fc45 100644 --- a/python-frontend/src/test/java/org/sonar/python/semantic/v2/TypeInferenceV2Test.java +++ b/python-frontend/src/test/java/org/sonar/python/semantic/v2/TypeInferenceV2Test.java @@ -154,6 +154,23 @@ void testUnresolvedImports() { .isEqualTo("something.unknown"); } + @Test + void testInheritanceFromUnresolvedImports() { + FileInput root = inferTypes(""" + from unknown import Parent + class A(Parent): ... + """); + + var classDef = (ClassDef) root.statements().statements().get(1); + assertThat(classDef.args().arguments().get(0)) + .extracting(RegularArgument.class::cast) + .extracting(RegularArgument::expression) + .extracting(Expression::typeV2) + .extracting(UnresolvedImportType.class::cast) + .extracting(UnresolvedImportType::importPath) + .isEqualTo("unknown.Parent"); + } + @Test void testProjectLevelSymbolTableImports() { var classSymbol = new ClassSymbolImpl("C", "something.known.C"); From ecd152150683b6860cff66313f0a25676dbb3177 Mon Sep 17 00:00:00 2001 From: Maksim Grebeniuk Date: Thu, 24 Oct 2024 09:42:35 +0200 Subject: [PATCH 2/3] add a test case from SONARPY-2244 --- .../python/semantic/v2/TypeInferenceV2Test.java | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/python-frontend/src/test/java/org/sonar/python/semantic/v2/TypeInferenceV2Test.java b/python-frontend/src/test/java/org/sonar/python/semantic/v2/TypeInferenceV2Test.java index 998cb6fc45..9992ae48c5 100644 --- a/python-frontend/src/test/java/org/sonar/python/semantic/v2/TypeInferenceV2Test.java +++ b/python-frontend/src/test/java/org/sonar/python/semantic/v2/TypeInferenceV2Test.java @@ -171,6 +171,21 @@ class A(Parent): ... .isEqualTo("unknown.Parent"); } + @Test + void testUnresolvedImportTypePropagationInsideFunctions() { + var fileInput = inferTypes(""" + from a import b + def function(): + f(b) + """); + var functionDef = (FunctionDef) fileInput.statements().statements().get(1); + var funcCall = ((ExpressionStatement) functionDef.body().statements().get(0)).expressions().get(0); + var arg = ((RegularArgument) ((CallExpression) funcCall).arguments().get(0)); + var argType = arg.expression().typeV2(); + + assertThat(argType).isInstanceOfSatisfying(UnresolvedImportType.class, a -> assertThat(a.importPath()).isEqualTo("a.b")); // FAILS + } + @Test void testProjectLevelSymbolTableImports() { var classSymbol = new ClassSymbolImpl("C", "something.known.C"); From bd28bef7b7c8beabd09111b99831a5c9256ef173 Mon Sep 17 00:00:00 2001 From: Maksim Grebeniuk Date: Fri, 25 Oct 2024 10:40:38 +0200 Subject: [PATCH 3/3] CR fixes --- .../semantic/v2/types/TrivialTypeInferenceVisitor.java | 9 ++++++++- .../sonar/python/semantic/v2/TypeInferenceV2Test.java | 2 +- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/python-frontend/src/main/java/org/sonar/python/semantic/v2/types/TrivialTypeInferenceVisitor.java b/python-frontend/src/main/java/org/sonar/python/semantic/v2/types/TrivialTypeInferenceVisitor.java index bfb615d362..ec6783a358 100644 --- a/python-frontend/src/main/java/org/sonar/python/semantic/v2/types/TrivialTypeInferenceVisitor.java +++ b/python-frontend/src/main/java/org/sonar/python/semantic/v2/types/TrivialTypeInferenceVisitor.java @@ -471,10 +471,17 @@ public void visitName(Name name) { .map(Expression.class::cast) .map(Expression::typeV2) // TODO: classes (SONARPY-1829) and functions should be propagated like other types - .filter(t -> (t instanceof ClassType) || (t instanceof FunctionType) || (t instanceof ModuleType) || (t instanceof UnknownType.UnresolvedImportType)) + .filter(TrivialTypeInferenceVisitor::shouldTypeBeEagerlyPropagated) .ifPresent(type -> setTypeToName(name, type)); } + private static boolean shouldTypeBeEagerlyPropagated(PythonType t) { + return (t instanceof ClassType) + || (t instanceof FunctionType) + || (t instanceof ModuleType) + || (t instanceof UnknownType.UnresolvedImportType); + } + private PythonType currentType() { return typeStack.peek(); } diff --git a/python-frontend/src/test/java/org/sonar/python/semantic/v2/TypeInferenceV2Test.java b/python-frontend/src/test/java/org/sonar/python/semantic/v2/TypeInferenceV2Test.java index 9992ae48c5..1f88b39369 100644 --- a/python-frontend/src/test/java/org/sonar/python/semantic/v2/TypeInferenceV2Test.java +++ b/python-frontend/src/test/java/org/sonar/python/semantic/v2/TypeInferenceV2Test.java @@ -183,7 +183,7 @@ def function(): var arg = ((RegularArgument) ((CallExpression) funcCall).arguments().get(0)); var argType = arg.expression().typeV2(); - assertThat(argType).isInstanceOfSatisfying(UnresolvedImportType.class, a -> assertThat(a.importPath()).isEqualTo("a.b")); // FAILS + assertThat(argType).isInstanceOfSatisfying(UnresolvedImportType.class, a -> assertThat(a.importPath()).isEqualTo("a.b")); } @Test