Skip to content

Commit a91a2d5

Browse files
authored
SONARPY-2242 TypeInferenceV2 should resolve stubs with variable descriptors pointing to submodules (#2094)
1 parent a95937e commit a91a2d5

File tree

6 files changed

+42
-5
lines changed

6 files changed

+42
-5
lines changed

python-frontend/src/main/java/org/sonar/python/index/VariableDescriptor.java

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,11 +26,17 @@ public class VariableDescriptor implements Descriptor {
2626
private final String name;
2727
private final String fullyQualifiedName;
2828
private final String annotatedType;
29+
private final boolean isImportedModule;
2930

30-
public VariableDescriptor(String name, @Nullable String fullyQualifiedName, @Nullable String annotatedType) {
31+
public VariableDescriptor(String name, @Nullable String fullyQualifiedName, @Nullable String annotatedType, boolean isImportedModule) {
3132
this.name = name;
3233
this.fullyQualifiedName = fullyQualifiedName;
3334
this.annotatedType = annotatedType;
35+
this.isImportedModule = isImportedModule;
36+
}
37+
38+
public VariableDescriptor(String name, @Nullable String fullyQualifiedName, @Nullable String annotatedType) {
39+
this(name, fullyQualifiedName, annotatedType, false);
3440
}
3541

3642
@Override
@@ -52,4 +58,8 @@ public Kind kind() {
5258
public String annotatedType() {
5359
return annotatedType;
5460
}
61+
62+
public boolean isImportedModule() {
63+
return isImportedModule;
64+
}
5565
}

python-frontend/src/main/java/org/sonar/python/semantic/v2/ProjectLevelTypeTable.java

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -67,10 +67,12 @@ public PythonType getType(List<String> typeFqnParts) {
6767
if (parent instanceof ModuleType moduleType) {
6868
TypeWrapper typeWrapper = moduleType.members().get(part);
6969
if (typeWrapper instanceof LazyTypeWrapper lazyTypeWrapper && !lazyTypeWrapper.isResolved()) {
70-
if (i == typeFqnParts.size() - 1) {
71-
// this is the name we are looking for, resolve it
70+
if (shouldResolveImmediately(lazyTypeWrapper, typeFqnParts, i)) {
71+
// We try to resolve the type of the member if it points to a different module.
72+
// If it points to the same module, we try to resolve the submodule of the same name
7273
return typeWrapper.type();
7374
}
75+
7476
// The member of the module is a LazyType, which means it's a re-exported type from a submodule
7577
// We try to resolve the submodule instead
7678
Optional<PythonType> subModule = moduleType.resolveSubmodule(part);
@@ -90,6 +92,10 @@ public PythonType getType(List<String> typeFqnParts) {
9092
return parent;
9193
}
9294

95+
private static boolean shouldResolveImmediately(LazyTypeWrapper lazyTypeWrapper, List<String> typeFqnParts, int i) {
96+
return i == typeFqnParts.size() - 1 && !(lazyTypeWrapper.hasImportPath(String.join(".", typeFqnParts)));
97+
}
98+
9399
/**
94100
* This method returns a module type for a given FQN, or unknown if it cannot be resolved.
95101
* It is to be used to retrieve modules referenced in the "from" clause of an "import from" statement,

python-frontend/src/main/java/org/sonar/python/semantic/v2/converter/VariableDescriptorToPythonTypeConverter.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,12 @@
2828
public class VariableDescriptorToPythonTypeConverter implements DescriptorToPythonTypeConverter {
2929

3030
public PythonType convert(ConversionContext ctx, VariableDescriptor from) {
31+
if (from.isImportedModule()) {
32+
var fqn = from.fullyQualifiedName();
33+
if (fqn != null) {
34+
return ctx.lazyTypesContext().getOrCreateLazyType(fqn);
35+
}
36+
}
3137
return Optional.ofNullable(from.annotatedType())
3238
.map(fqn -> ctx.lazyTypesContext().getOrCreateLazyTypeWrapper(fqn))
3339
.map(t -> (PythonType) new ObjectType(t))

python-frontend/src/main/java/org/sonar/python/semantic/v2/typeshed/VarSymbolToDescriptorConverter.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,10 +28,11 @@ public class VarSymbolToDescriptorConverter {
2828
public Descriptor convert(SymbolsProtos.VarSymbol varSymbol) {
2929
var fullyQualifiedName = TypeShedUtils.normalizedFqn(varSymbol.getFullyQualifiedName());
3030
var typeAnnotation = TypeShedUtils.getTypesNormalizedFqn(varSymbol.getTypeAnnotation());
31+
var isImportedModule = varSymbol.getIsImportedModule();
3132
if (isTypeAnnotationKnownToBeIncorrect(fullyQualifiedName)) {
32-
return new VariableDescriptor(varSymbol.getName(), fullyQualifiedName, null);
33+
return new VariableDescriptor(varSymbol.getName(), fullyQualifiedName, null, isImportedModule);
3334
}
34-
return new VariableDescriptor(varSymbol.getName(), fullyQualifiedName, typeAnnotation);
35+
return new VariableDescriptor(varSymbol.getName(), fullyQualifiedName, typeAnnotation, isImportedModule);
3536
}
3637

3738
private static boolean isTypeAnnotationKnownToBeIncorrect(String fullyQualifiedName) {

python-frontend/src/main/java/org/sonar/python/types/v2/LazyTypeWrapper.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,10 @@ public boolean isResolved() {
4646
return !(type instanceof LazyType);
4747
}
4848

49+
public boolean hasImportPath(String importPath) {
50+
return ((LazyType) type).importPath().equals(importPath);
51+
}
52+
4953
@Override
5054
public boolean equals(Object o) {
5155
if (this == o) return true;

python-frontend/src/test/java/org/sonar/python/semantic/v2/ProjectLevelTypeTableTest.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -253,4 +253,14 @@ class lib: ...
253253
// SONARPY-2176 lib should be resolved as the renamed class "A" here
254254
assertThat(aType.name()).isEqualTo("A");
255255
}
256+
257+
@Test
258+
void resolveStubsWithImportedModuleVariableDescriptor() {
259+
var symbolTable = ProjectLevelSymbolTable.empty();
260+
var table = new ProjectLevelTypeTable(symbolTable);
261+
262+
var nnModuleType = table.getType("torch.nn");
263+
264+
Assertions.assertThat(nnModuleType).isNotNull().isInstanceOf(ModuleType.class);
265+
}
256266
}

0 commit comments

Comments
 (0)