Skip to content

Commit b540f4e

Browse files
ghislainpiotguillaume-dequenne
authored andcommitted
SONARPY-2124 Ensure parameters types of imported project functions have their declared type populated. (#1972)
1 parent c484eb2 commit b540f4e

File tree

3 files changed

+45
-5
lines changed

3 files changed

+45
-5
lines changed

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

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -49,8 +49,8 @@
4949
import org.sonar.python.types.v2.ObjectType;
5050
import org.sonar.python.types.v2.ParameterV2;
5151
import org.sonar.python.types.v2.PythonType;
52-
import org.sonar.python.types.v2.SimpleTypeWrapper;
5352
import org.sonar.python.types.v2.TypeOrigin;
53+
import org.sonar.python.types.v2.TypeWrapper;
5454
import org.sonar.python.types.v2.UnionType;
5555

5656
public class SymbolsModuleTypeProvider {
@@ -121,7 +121,7 @@ private PythonType convertToFunctionType(FunctionSymbol symbol, Map<Symbol, Pyth
121121

122122
var parameters = symbol.parameters()
123123
.stream()
124-
.map(SymbolsModuleTypeProvider::convertParameter)
124+
.map(this::convertParameter)
125125
.toList();
126126

127127
var returnType = getReturnTypeFromSymbol(symbol);
@@ -209,15 +209,19 @@ private PythonType convertToClassType(ClassSymbol symbol, Map<Symbol, PythonType
209209
return classType;
210210
}
211211

212-
private static ParameterV2 convertParameter(FunctionSymbol.Parameter parameter) {
212+
private ParameterV2 convertParameter(FunctionSymbol.Parameter parameter) {
213+
var typeWrapper = Optional.ofNullable(((FunctionSymbolImpl.ParameterImpl) parameter).annotatedTypeName())
214+
.map(lazyTypesContext::getOrCreateLazyTypeWrapper)
215+
.orElse(TypeWrapper.UNKNOWN_TYPE_WRAPPER);
216+
213217
return new ParameterV2(parameter.name(),
214-
new SimpleTypeWrapper(PythonType.UNKNOWN),
218+
typeWrapper,
215219
parameter.hasDefaultValue(),
216220
parameter.isKeywordOnly(),
217221
parameter.isPositionalOnly(),
218222
parameter.isKeywordVariadic(),
219223
parameter.isPositionalVariadic(),
220-
null);
224+
parameter.location());
221225
}
222226

223227
private PythonType convertToUnionType(AmbiguousSymbol ambiguousSymbol, Map<Symbol, PythonType> createdTypesBySymbol) {

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,4 +22,6 @@
2222
public interface TypeWrapper {
2323

2424
PythonType type();
25+
26+
TypeWrapper UNKNOWN_TYPE_WRAPPER = new SimpleTypeWrapper(PythonType.UNKNOWN);
2527
}

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

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
import org.junit.jupiter.api.Disabled;
3232
import org.junit.jupiter.api.Test;
3333
import org.mockito.Mockito;
34+
import org.sonar.plugins.python.api.LocationInFile;
3435
import org.sonar.plugins.python.api.PythonFile;
3536
import org.sonar.plugins.python.api.symbols.ClassSymbol;
3637
import org.sonar.plugins.python.api.symbols.Symbol;
@@ -54,6 +55,7 @@
5455
import org.sonar.python.PythonTestUtils;
5556
import org.sonar.python.semantic.ClassSymbolImpl;
5657
import org.sonar.python.semantic.ProjectLevelSymbolTable;
58+
import org.sonar.python.semantic.SymbolUtils;
5759
import org.sonar.python.tree.ExpressionStatementImpl;
5860
import org.sonar.python.tree.TreeUtils;
5961
import org.sonar.python.types.v2.ClassType;
@@ -694,6 +696,38 @@ def foo(param: my_alias): ...
694696
assertThat(((FunctionType) functionDef.name().typeV2()).parameters().get(0).declaredType().type().unwrappedType()).isEqualTo(PythonType.UNKNOWN);
695697
}
696698

699+
@Test
700+
void inferFunctionParameterTypesMultiFile() {
701+
FileInput tree = parseWithoutSymbols(
702+
"def foo(param1: int): ...",
703+
"class A: ...",
704+
"def foo2(p1: dict, p2: A): ..."
705+
);
706+
ProjectLevelSymbolTable projectLevelSymbolTable = new ProjectLevelSymbolTable();
707+
var modFile = pythonFile("mod.py");
708+
projectLevelSymbolTable.addModule(tree, "", modFile);
709+
ProjectLevelTypeTable projectLevelTypeTable = new ProjectLevelTypeTable(projectLevelSymbolTable);
710+
var modFileId = SymbolUtils.pathOf(modFile).toString();
711+
712+
var intType = projectLevelTypeTable.lazyTypesContext().getOrCreateLazyType("int").resolve();
713+
var dictType = projectLevelTypeTable.lazyTypesContext().getOrCreateLazyType("dict").resolve();
714+
var aType = projectLevelTypeTable.lazyTypesContext().getOrCreateLazyType("mod.A").resolve();
715+
var lines = """
716+
from mod import foo, foo2
717+
foo
718+
foo2
719+
""";
720+
FileInput fileInput = inferTypes(lines, projectLevelTypeTable);
721+
FunctionType fooType = (FunctionType) ((ExpressionStatement) fileInput.statements().statements().get(1)).expressions().get(0).typeV2();
722+
assertThat(fooType.parameters().get(0).declaredType().type().unwrappedType()).isEqualTo(intType);
723+
724+
FunctionType foo2Type = (FunctionType) ((ExpressionStatement) fileInput.statements().statements().get(2)).expressions().get(0).typeV2();
725+
assertThat(foo2Type.parameters()).extracting(ParameterV2::declaredType).extracting(TypeWrapper::type).containsExactly(dictType, aType);
726+
assertThat(foo2Type.parameters()).extracting(ParameterV2::location).containsExactly(
727+
new LocationInFile(modFileId, 3, 9, 3, 17),
728+
new LocationInFile(modFileId, 3, 19, 3, 24));
729+
}
730+
697731
@Test
698732
void inferFunctionReturnTypeType() {
699733
FileInput root = inferTypes("""

0 commit comments

Comments
 (0)