3535import org .sonar .plugins .python .api .symbols .ClassSymbol ;
3636import org .sonar .plugins .python .api .symbols .FunctionSymbol ;
3737import org .sonar .plugins .python .api .symbols .Symbol ;
38- import org .sonar .plugins .python .api .types .InferredType ;
3938import org .sonar .python .semantic .ClassSymbolImpl ;
4039import org .sonar .python .semantic .FunctionSymbolImpl ;
4140import org .sonar .python .semantic .ProjectLevelSymbolTable ;
4241import org .sonar .python .semantic .SymbolImpl ;
42+ import org .sonar .python .types .InferredTypes ;
43+ import org .sonar .python .types .protobuf .SymbolsProtos ;
4344import org .sonar .python .types .v2 .ClassType ;
4445import org .sonar .python .types .v2 .FunctionType ;
4546import 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}
0 commit comments