3232import java .util .stream .Collectors ;
3333import org .sonar .plugins .python .api .PythonFile ;
3434import org .sonar .plugins .python .api .cfg .ControlFlowGraph ;
35+ import org .sonar .plugins .python .api .symbols .FunctionSymbol ;
3536import org .sonar .plugins .python .api .symbols .Symbol ;
3637import org .sonar .plugins .python .api .symbols .Usage ;
3738import org .sonar .plugins .python .api .tree .AssignmentStatement ;
4142import org .sonar .plugins .python .api .tree .FileInput ;
4243import org .sonar .plugins .python .api .tree .FunctionDef ;
4344import org .sonar .plugins .python .api .tree .Name ;
45+ import org .sonar .plugins .python .api .tree .Parameter ;
4446import org .sonar .plugins .python .api .tree .QualifiedExpression ;
4547import org .sonar .plugins .python .api .tree .Tree ;
4648import org .sonar .plugins .python .api .tree .TryStatement ;
4749import org .sonar .plugins .python .api .types .InferredType ;
4850import org .sonar .python .semantic .SymbolImpl ;
51+ import org .sonar .python .tree .FunctionDefImpl ;
4952import org .sonar .python .tree .NameImpl ;
53+ import org .sonar .python .tree .TreeUtils ;
5054
5155public class TypeInference extends BaseTreeVisitor {
5256
@@ -58,6 +62,7 @@ public class TypeInference extends BaseTreeVisitor {
5862 private final Map <Symbol , Set <Assignment >> assignmentsByLhs = new HashMap <>();
5963 private final Map <QualifiedExpression , MemberAccess > memberAccessesByQualifiedExpr = new HashMap <>();
6064 private final Map <AssignmentStatement , Assignment > assignmentsByAssignmentStatement = new HashMap <>();
65+ private Map <String , InferredType > parameterTypesByName = new HashMap <>();
6166
6267 public static void inferTypes (FileInput fileInput , PythonFile pythonFile ) {
6368 fileInput .accept (new BaseTreeVisitor () {
@@ -67,7 +72,6 @@ public void visitFunctionDef(FunctionDef funcDef) {
6772 inferTypesAndMemberAccessSymbols (funcDef , pythonFile );
6873 }
6974 });
70-
7175 fileInput .accept (new BaseTreeVisitor () {
7276 @ Override
7377 public void visitQualifiedExpression (QualifiedExpression qualifiedExpression ) {
@@ -82,28 +86,32 @@ public void visitQualifiedExpression(QualifiedExpression qualifiedExpression) {
8286 });
8387 }
8488
85- private static void inferTypesAndMemberAccessSymbols (FunctionDef functionDef , PythonFile pythonFile ) {
86- TypeInference visitor = new TypeInference ();
87- functionDef .accept (visitor );
89+ private static Set <Symbol > getTrackedVars (Set <Symbol > localVariables , Set <Name > assignedNames ) {
8890 Set <Symbol > trackedVars = new HashSet <>();
89- Set <Name > assignedNames = visitor .assignmentsByLhs .values ().stream ()
90- .flatMap (Collection ::stream )
91- .map (a -> a .lhsName )
92- .collect (Collectors .toSet ());
93- for (Symbol variable : functionDef .localVariables ()) {
91+ for (Symbol variable : localVariables ) {
9492 boolean hasMissingBindingUsage = variable .usages ().stream ()
9593 .filter (Usage ::isBindingUsage )
9694 .anyMatch (u -> !assignedNames .contains (u .tree ()));
9795 if (!hasMissingBindingUsage ) {
9896 trackedVars .add (variable );
9997 }
10098 }
99+ return trackedVars ;
100+ }
101+
102+ private static void inferTypesAndMemberAccessSymbols (FunctionDef functionDef , PythonFile pythonFile ) {
103+ TypeInference visitor = new TypeInference ();
104+ functionDef .accept (visitor );
105+ Set <Name > assignedNames = visitor .assignmentsByLhs .values ().stream ()
106+ .flatMap (Collection ::stream )
107+ .map (a -> a .lhsName )
108+ .collect (Collectors .toSet ());
101109
102110 TryStatementVisitor tryStatementVisitor = new TryStatementVisitor (functionDef );
103111 functionDef .body ().accept (tryStatementVisitor );
104112 if (tryStatementVisitor .hasTryStatement ) {
105113 // CFG doesn't model precisely try-except statements. Hence we fallback to AST based type inference
106- visitor .processPropagations (trackedVars );
114+ visitor .processPropagations (getTrackedVars ( functionDef . localVariables (), assignedNames ) );
107115 functionDef .body ().accept (new BaseTreeVisitor () {
108116 @ Override
109117 public void visitFunctionDef (FunctionDef visited ) {
@@ -122,7 +130,12 @@ public void visitName(Name name) {
122130 if (cfg == null ) {
123131 return ;
124132 }
125- visitor .flowSensitiveTypeInference (cfg , trackedVars );
133+ Set <Name > annotatedParamNames = TreeUtils .nonTupleParameters (functionDef ).stream ()
134+ .filter (parameter -> parameter .typeAnnotation () != null )
135+ .map (Parameter ::name )
136+ .collect (Collectors .toSet ());
137+ assignedNames .addAll (annotatedParamNames );
138+ visitor .flowSensitiveTypeInference (cfg , getTrackedVars (functionDef .localVariables (), assignedNames ), functionDef );
126139 }
127140 }
128141
@@ -198,9 +211,15 @@ public void visitQualifiedExpression(QualifiedExpression qualifiedExpression) {
198211 * if i > 0: b = a.capitalize() # at the end of second execution, type of b is inferred to be "STR"
199212 * else: a = 'abc'
200213 */
201- private void flowSensitiveTypeInference (ControlFlowGraph cfg , Set <Symbol > trackedVars ) {
214+ private void flowSensitiveTypeInference (ControlFlowGraph cfg , Set <Symbol > trackedVars , FunctionDef functionDef ) {
215+ Optional .ofNullable (((FunctionDefImpl ) functionDef ).functionSymbol ()).ifPresent (functionSymbol ->
216+ parameterTypesByName = functionSymbol .parameters ()
217+ .stream ()
218+ .filter (parameter -> parameter .name () != null )
219+ .collect (Collectors .toMap (FunctionSymbol .Parameter ::name , FunctionSymbol .Parameter ::declaredType )));
220+
202221 FlowSensitiveTypeInference flowSensitiveTypeInference =
203- new FlowSensitiveTypeInference (trackedVars , memberAccessesByQualifiedExpr , assignmentsByAssignmentStatement );
222+ new FlowSensitiveTypeInference (trackedVars , memberAccessesByQualifiedExpr , assignmentsByAssignmentStatement , parameterTypesByName );
204223
205224 flowSensitiveTypeInference .compute (cfg );
206225 flowSensitiveTypeInference .compute (cfg );
0 commit comments