35
35
import org .sonar .plugins .python .api .symbols .ClassSymbol ;
36
36
import org .sonar .plugins .python .api .symbols .FunctionSymbol ;
37
37
import org .sonar .plugins .python .api .symbols .Symbol ;
38
- import org .sonar .plugins .python .api .types .InferredType ;
39
38
import org .sonar .python .semantic .ClassSymbolImpl ;
40
39
import org .sonar .python .semantic .FunctionSymbolImpl ;
41
40
import org .sonar .python .semantic .ProjectLevelSymbolTable ;
42
41
import org .sonar .python .semantic .SymbolImpl ;
42
+ import org .sonar .python .types .InferredTypes ;
43
+ import org .sonar .python .types .protobuf .SymbolsProtos ;
43
44
import org .sonar .python .types .v2 .ClassType ;
44
45
import org .sonar .python .types .v2 .FunctionType ;
45
46
import org .sonar .python .types .v2 .LazyType ;
@@ -120,9 +121,11 @@ private PythonType convertToFunctionType(FunctionSymbol symbol, Map<Symbol, Pyth
120
121
.map (SymbolsModuleTypeProvider ::convertParameter )
121
122
.toList ();
122
123
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
+ }
126
129
TypeOrigin typeOrigin = symbol .isStub () ? TypeOrigin .STUB : TypeOrigin .LOCAL ;
127
130
128
131
FunctionTypeBuilder functionTypeBuilder =
@@ -144,20 +147,6 @@ private PythonType convertToFunctionType(FunctionSymbol symbol, Map<Symbol, Pyth
144
147
return functionType ;
145
148
}
146
149
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
-
161
150
PythonType resolvePossibleLazyType (String fullyQualifiedName ) {
162
151
if (rootModule == null ) {
163
152
// If root module has not been created yet, return lazy type
@@ -245,4 +234,43 @@ private PythonType convertToType(Symbol symbol, Map<Symbol, PythonType> createdT
245
234
case OTHER -> PythonType .UNKNOWN ;
246
235
};
247
236
}
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
+ }
248
276
}
0 commit comments