Skip to content

Commit fdfc035

Browse files
Seppli11sonartech
authored andcommitted
SONARPY-2423 AliasDescriptor should produce identical types to their original types
GitOrigin-RevId: 90b7331a6400496d1b675cf324943d264c984d61
1 parent dde0114 commit fdfc035

18 files changed

Lines changed: 964 additions & 20 deletions

File tree

python-commons/src/test/java/org/sonar/plugins/python/PythonSensorTest.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -998,7 +998,7 @@ void test_typeshed_stubs_information_is_saved_to_cache() {
998998
assertThat(resolvedTypeshedModules).containsExactlyInAnyOrder(
999999
"typing", "math",
10001000
"django", "django.urls.conf", "django.urls",
1001-
"fastapi", "fastapi.responses", "fastapi.responses.HTMLResponse");
1001+
"fastapi", "fastapi.responses", "fastapi.responses.HTMLResponse", "starlette.responses", "starlette");
10021002
}
10031003

10041004
@Test

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

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,14 +16,18 @@
1616
*/
1717
package org.sonar.python.semantic.v2.converter;
1818

19+
import org.sonar.plugins.python.api.types.v2.PythonType;
1920
import org.sonar.python.index.AliasDescriptor;
2021
import org.sonar.python.index.Descriptor;
21-
import org.sonar.plugins.python.api.types.v2.PythonType;
2222

2323
public class AliasDescriptorToPythonTypeConverter implements DescriptorToPythonTypeConverter {
2424
@Override
2525
public PythonType convert(ConversionContext ctx, Descriptor from) {
26-
// SONARPY-2423: We should try to retrieve the original type if possible, instead of recreating it
26+
AliasDescriptor aliasDescriptor = (AliasDescriptor) from;
27+
String originalFqn = aliasDescriptor.originalDescriptor().fullyQualifiedName();
28+
if (originalFqn != null && !originalFqn.startsWith(ctx.moduleFqn())) {
29+
return ctx.lazyTypesContext().getOrCreateLazyType(originalFqn);
30+
}
2731
return ctx.convert(((AliasDescriptor) from).originalDescriptor());
2832
}
2933
}

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

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,17 +16,28 @@
1616
*/
1717
package org.sonar.python.semantic.v2.converter;
1818

19+
import java.util.Set;
1920
import java.util.stream.Collectors;
20-
import org.sonar.python.index.AmbiguousDescriptor;
21-
import org.sonar.python.index.Descriptor;
2221
import org.sonar.plugins.python.api.types.v2.PythonType;
2322
import org.sonar.plugins.python.api.types.v2.UnionType;
23+
import org.sonar.python.index.AmbiguousDescriptor;
24+
import org.sonar.python.index.Descriptor;
25+
import org.sonar.python.types.v2.LazyType;
26+
import org.sonar.python.types.v2.LazyUnionType;
2427

2528
public class AmbiguousDescriptorToPythonTypeConverter implements DescriptorToPythonTypeConverter {
2629

2730
public PythonType convert(ConversionContext ctx, AmbiguousDescriptor from) {
2831
var candidates = from.alternatives().stream().map(ctx::convert).collect(Collectors.toSet());
29-
return UnionType.or(candidates);
32+
if (containsLazyType(candidates)) {
33+
return LazyUnionType.or(candidates);
34+
} else {
35+
return UnionType.or(candidates);
36+
}
37+
}
38+
39+
private static boolean containsLazyType(Set<PythonType> types) {
40+
return types.stream().anyMatch(type -> type instanceof LazyType || (type instanceof UnionType unionType && containsLazyType(unionType.candidates())));
3041
}
3142

3243
@Override

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ public PythonType convert(ConversionContext context, TypeAnnotationDescriptor ty
5555
// this should be handled as a function type - see SONARPY-953
5656
return context.lazyTypesContext().getOrCreateLazyType("function");
5757
case UNION:
58-
return new LazyUnionType(type.args().stream().map(t -> convert(context, t)).collect(Collectors.toSet()));
58+
return LazyUnionType.or(type.args().stream().map(t -> convert(context, t)).collect(Collectors.toSet()));
5959
case TUPLE:
6060
return context.lazyTypesContext().getOrCreateLazyType("tuple");
6161
case NONE:

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

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,10 @@
1717
package org.sonar.python.types.v2;
1818

1919
import com.google.common.annotations.VisibleForTesting;
20+
import java.util.Collection;
2021
import java.util.Collections;
2122
import java.util.HashSet;
23+
import java.util.Objects;
2224
import java.util.Set;
2325
import java.util.stream.Collectors;
2426
import java.util.stream.Stream;
@@ -29,10 +31,11 @@ public class LazyUnionType implements PythonType, ResolvableType {
2931

3032
private final Set<PythonType> candidates;
3133

32-
public LazyUnionType(Set<PythonType> candidates) {
33-
this.candidates = candidates.stream().flatMap(LazyUnionType::flattenLazyUnionTypes).collect(Collectors.toCollection(HashSet::new));
34+
private LazyUnionType(Set<PythonType> candidates) {
35+
this.candidates = candidates;
3436
}
3537

38+
@Override
3639
public PythonType resolve() {
3740
Set<PythonType> resolvedCandidates = new HashSet<>();
3841
for (PythonType candidate : candidates) {
@@ -55,4 +58,20 @@ private static Stream<PythonType> flattenLazyUnionTypes(PythonType type) {
5558
protected Set<PythonType> candidates() {
5659
return Collections.unmodifiableSet(candidates);
5760
}
61+
62+
public static PythonType or(Collection<PythonType> types) {
63+
types = types.stream().filter(Objects::nonNull).collect(Collectors.toSet());
64+
if (types.isEmpty()) {
65+
return PythonType.UNKNOWN;
66+
}
67+
if (types.size() == 1) {
68+
return types.iterator().next();
69+
}
70+
71+
Set<PythonType> flatTypes = types.stream().flatMap(LazyUnionType::flattenLazyUnionTypes).collect(Collectors.toSet());
72+
if (flatTypes.stream().anyMatch(type -> type == PythonType.UNKNOWN)) {
73+
return PythonType.UNKNOWN;
74+
}
75+
return new LazyUnionType(flatTypes);
76+
}
5877
}

0 commit comments

Comments
 (0)