Skip to content

Commit 4fc70a7

Browse files
SONARPY-2069 Fix usage of V1 TypeShed during resolution of V2 types (#1924)
1 parent fc84068 commit 4fc70a7

File tree

5 files changed

+55
-24
lines changed

5 files changed

+55
-24
lines changed

python-frontend/src/main/java/org/sonar/python/semantic/FunctionSymbolImpl.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -321,6 +321,10 @@ public InferredType declaredReturnType() {
321321
}
322322
return declaredReturnType;
323323
}
324+
325+
public SymbolsProtos.Type protobufReturnType() {
326+
return protobufReturnType;
327+
}
324328

325329
static class ParameterType {
326330
InferredType inferredType;

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

Lines changed: 46 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -35,11 +35,12 @@
3535
import org.sonar.plugins.python.api.symbols.ClassSymbol;
3636
import org.sonar.plugins.python.api.symbols.FunctionSymbol;
3737
import org.sonar.plugins.python.api.symbols.Symbol;
38-
import org.sonar.plugins.python.api.types.InferredType;
3938
import org.sonar.python.semantic.ClassSymbolImpl;
4039
import org.sonar.python.semantic.FunctionSymbolImpl;
4140
import org.sonar.python.semantic.ProjectLevelSymbolTable;
4241
import org.sonar.python.semantic.SymbolImpl;
42+
import org.sonar.python.types.InferredTypes;
43+
import org.sonar.python.types.protobuf.SymbolsProtos;
4344
import org.sonar.python.types.v2.ClassType;
4445
import org.sonar.python.types.v2.FunctionType;
4546
import org.sonar.python.types.v2.LazyType;
@@ -120,9 +121,11 @@ private PythonType convertToFunctionType(FunctionSymbol symbol, Map<Symbol, Pyth
120121
.map(SymbolsModuleTypeProvider::convertParameter)
121122
.toList();
122123

123-
InferredType inferredType = ((FunctionSymbolImpl) symbol).declaredReturnType();
124-
ClassSymbol classSymbol = inferredType.runtimeTypeSymbol();
125-
var returnType = resolveReturnType(createdTypesBySymbol, classSymbol);
124+
var returnType = PythonType.UNKNOWN;
125+
var protoReturnType = ((FunctionSymbolImpl) symbol).protobufReturnType();
126+
if (protoReturnType != null) {
127+
returnType = convertProtobufType(protoReturnType);
128+
}
126129
TypeOrigin typeOrigin = symbol.isStub() ? TypeOrigin.STUB : TypeOrigin.LOCAL;
127130

128131
FunctionTypeBuilder functionTypeBuilder =
@@ -144,20 +147,6 @@ private PythonType convertToFunctionType(FunctionSymbol symbol, Map<Symbol, Pyth
144147
return functionType;
145148
}
146149

147-
private PythonType resolveReturnType(Map<Symbol, PythonType> createdTypesBySymbol, @Nullable ClassSymbol classSymbol) {
148-
if (classSymbol == null) {
149-
return PythonType.UNKNOWN;
150-
}
151-
if (createdTypesBySymbol.containsKey(classSymbol)) {
152-
return createdTypesBySymbol.get(classSymbol);
153-
}
154-
String fullyQualifiedName = classSymbol.fullyQualifiedName();
155-
if (fullyQualifiedName == null) {
156-
return convertToType(classSymbol, createdTypesBySymbol);
157-
}
158-
return resolvePossibleLazyType(fullyQualifiedName);
159-
}
160-
161150
PythonType resolvePossibleLazyType(String fullyQualifiedName) {
162151
if (rootModule == null) {
163152
// If root module has not been created yet, return lazy type
@@ -245,4 +234,43 @@ private PythonType convertToType(Symbol symbol, Map<Symbol, PythonType> createdT
245234
case OTHER -> PythonType.UNKNOWN;
246235
};
247236
}
237+
238+
public PythonType convertProtobufType(SymbolsProtos.Type type) {
239+
switch (type.getKind()) {
240+
case INSTANCE:
241+
String typeName = type.getFullyQualifiedName();
242+
// _SpecialForm is the type used for some special types, like Callable, Union, TypeVar, ...
243+
// It comes from CPython impl: https://github.com/python/cpython/blob/e39ae6bef2c357a88e232dcab2e4b4c0f367544b/Lib/typing.py#L439
244+
// This doesn't seem to be very precisely specified in typeshed, because it has special semantic.
245+
// To avoid FPs, we treat it as ANY
246+
if ("typing._SpecialForm".equals(typeName)) {
247+
return PythonType.UNKNOWN;
248+
}
249+
typeName = typeName.replaceFirst("^builtins\\.", "");
250+
return typeName.isEmpty() ? PythonType.UNKNOWN : resolvePossibleLazyType(typeName);
251+
case TYPE:
252+
return resolvePossibleLazyType("type");
253+
case TYPE_ALIAS:
254+
return convertProtobufType(type.getArgs(0));
255+
case CALLABLE:
256+
// this should be handled as a function type - see SONARPY-953
257+
return PythonType.UNKNOWN;
258+
case UNION:
259+
return UnionType.or(type.getArgsList().stream().map(this::convertProtobufType).collect(Collectors.toSet()));
260+
case TUPLE:
261+
return resolvePossibleLazyType("tuple");
262+
case NONE:
263+
return resolvePossibleLazyType("NoneType");
264+
case TYPED_DICT:
265+
return resolvePossibleLazyType("dict");
266+
case TYPE_VAR:
267+
return Optional.of(type)
268+
.filter(InferredTypes::filterTypeVar)
269+
.map(SymbolsProtos.Type::getFullyQualifiedName)
270+
.map(this::resolvePossibleLazyType)
271+
.orElse(PythonType.UNKNOWN);
272+
default:
273+
return PythonType.UNKNOWN;
274+
}
275+
}
248276
}

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,7 @@ public class TypeShed {
9191
public TypeShed(ProjectLevelSymbolTable projectLevelSymbolTable) {
9292
// workaround to initialize supported python versions used in ClassSymbolImpl
9393
// TODO: remove once v2 types model will be populated from TypeShed bypassing conversion to symbols
94-
builtins = org.sonar.python.types.TypeShed.builtinSymbols();
94+
org.sonar.python.types.TypeShed.builtinSymbols();
9595
typeShedSymbols = new HashMap<>();
9696
modulesInProgress = new HashSet<>();
9797
this.projectLevelSymbolTable = projectLevelSymbolTable;

python-frontend/src/main/java/org/sonar/python/types/InferredTypes.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -245,7 +245,7 @@ public static InferredType fromTypeshedProtobuf(SymbolsProtos.Type type) {
245245
"^builtins\\.object$",
246246
"^_ctypes\\._CanCastTo$"); // ref: SONARPY-1477
247247

248-
private static boolean filterTypeVar(SymbolsProtos.Type type) {
248+
public static boolean filterTypeVar(SymbolsProtos.Type type) {
249249
return Optional.of(type)
250250
// Filtering self returning methods until the SONARPY-1472 will be solved
251251
.filter(Predicate.not(t -> t.getPrettyPrintedName().endsWith(".Self")))

sonar-python-plugin/src/main/java/org/sonar/plugins/python/SymbolVisitor.java

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,17 +24,16 @@
2424
import java.util.List;
2525
import org.sonar.api.batch.sensor.symbol.NewSymbol;
2626
import org.sonar.api.batch.sensor.symbol.NewSymbolTable;
27+
import org.sonar.plugins.python.api.symbols.Symbol;
28+
import org.sonar.plugins.python.api.symbols.Usage;
29+
import org.sonar.plugins.python.api.tree.BaseTreeVisitor;
2730
import org.sonar.plugins.python.api.tree.ClassDef;
2831
import org.sonar.plugins.python.api.tree.ComprehensionExpression;
2932
import org.sonar.plugins.python.api.tree.DictCompExpression;
3033
import org.sonar.plugins.python.api.tree.FileInput;
3134
import org.sonar.plugins.python.api.tree.FunctionDef;
3235
import org.sonar.plugins.python.api.tree.LambdaExpression;
3336
import org.sonar.plugins.python.api.tree.Tree;
34-
import org.sonar.plugins.python.api.symbols.Symbol;
35-
import org.sonar.plugins.python.api.symbols.Usage;
36-
import org.sonar.plugins.python.api.tree.BaseTreeVisitor;
37-
import org.sonar.python.tree.DictCompExpressionImpl;
3837

3938
public class SymbolVisitor extends BaseTreeVisitor {
4039

0 commit comments

Comments
 (0)