Skip to content

Commit 6ccd966

Browse files
committed
move Propagation#propagate(...) into AstBasedPropgation
1 parent 14db014 commit 6ccd966

File tree

2 files changed

+73
-66
lines changed

2 files changed

+73
-66
lines changed

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

Lines changed: 73 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,19 +22,30 @@
2222
import java.util.HashSet;
2323
import java.util.Iterator;
2424
import java.util.Map;
25+
import java.util.Optional;
2526
import java.util.Set;
2627
import java.util.stream.Collectors;
28+
import java.util.stream.Stream;
29+
import javax.annotation.CheckForNull;
30+
import org.sonar.plugins.python.api.tree.Expression;
31+
import org.sonar.plugins.python.api.tree.Name;
32+
import org.sonar.plugins.python.api.tree.Tree;
2733
import org.sonar.python.semantic.v2.SymbolV2;
34+
import org.sonar.python.semantic.v2.SymbolV2Utils;
2835
import org.sonar.python.semantic.v2.TypeTable;
36+
import org.sonar.python.semantic.v2.UsageV2;
37+
import org.sonar.python.tree.NameImpl;
38+
import org.sonar.python.tree.TreeUtils;
2939
import org.sonar.python.types.v2.PythonType;
40+
import org.sonar.python.types.v2.UnionType;
3041

3142
public class AstBasedPropagation {
3243
private final Map<SymbolV2, Set<Propagation>> propagationsByLhs;
33-
private final TypeTable typeTable;
44+
private final Propagator propagator;
3445

3546
public AstBasedPropagation(Map<SymbolV2, Set<Propagation>> propagationsByLhs, TypeTable typeTable) {
3647
this.propagationsByLhs = propagationsByLhs;
37-
this.typeTable = typeTable;
48+
this.propagator = new Propagator(typeTable);
3849
}
3950

4051
public Map<SymbolV2, Set<PythonType>> processPropagations(Set<SymbolV2> trackedVars) {
@@ -56,18 +67,76 @@ public Map<SymbolV2, Set<PythonType>> processPropagations(Set<SymbolV2> trackedV
5667
return propagations.stream().collect(Collectors.groupingBy(Propagation::lhsSymbol, Collectors.mapping(Propagation::rhsType, Collectors.toSet())));
5768
}
5869

59-
private static void applyPropagations(Set<Propagation> propagations, Set<SymbolV2> initializedVars, boolean checkDependenciesReadiness) {
70+
private void applyPropagations(Set<Propagation> propagations, Set<SymbolV2> initializedVars, boolean checkDependenciesReadiness) {
6071
Set<Propagation> workSet = new HashSet<>(propagations);
6172
while (!workSet.isEmpty()) {
6273
Iterator<Propagation> iterator = workSet.iterator();
6374
Propagation propagation = iterator.next();
6475
iterator.remove();
6576
if (!checkDependenciesReadiness || propagation.areDependenciesReady(initializedVars)) {
66-
boolean learnt = propagation.propagate(initializedVars);
77+
boolean learnt = propagator.propagate(propagation, initializedVars);
6778
if (learnt) {
6879
workSet.addAll(propagation.dependents());
6980
}
7081
}
7182
}
7283
}
84+
85+
private record Propagator(TypeTable typeTable) {
86+
/**
87+
* @return true if the propagation effectively changed the inferred type of assignment LHS
88+
*/
89+
public boolean propagate(Propagation propagation, Set<SymbolV2> initializedVars) {
90+
PythonType rhsType = propagation.rhsType();
91+
Name lhsName = propagation.lhsName();
92+
SymbolV2 lhsSymbol = propagation.lhsSymbol();
93+
if (initializedVars.add(lhsSymbol)) {
94+
propagateTypeToUsages(propagation, rhsType);
95+
return true;
96+
} else {
97+
PythonType currentType = currentType(lhsName);
98+
if (currentType == null) {
99+
return false;
100+
}
101+
PythonType newType = UnionType.or(rhsType, currentType);
102+
propagateTypeToUsages(propagation, newType);
103+
return !newType.equals(currentType);
104+
}
105+
}
106+
107+
private void propagateTypeToUsages(Propagation propagation, PythonType newType) {
108+
Tree scopeTree = propagation.scopeTree(propagation.lhsName());
109+
getSymbolNonDeclarationUsageTrees(propagation.lhsSymbol)
110+
.filter(NameImpl.class::isInstance)
111+
.map(NameImpl.class::cast)
112+
// Avoid propagation to usages in nested scopes, as this may lead to FPs
113+
.filter(n -> isInSameScope(propagation, n, scopeTree))
114+
.forEach(n -> n.typeV2(newType));
115+
}
116+
117+
@CheckForNull
118+
private static PythonType currentType(Name lhsName) {
119+
return Optional.ofNullable(lhsName.symbolV2())
120+
.stream()
121+
.flatMap(Propagator::getSymbolNonDeclarationUsageTrees)
122+
.flatMap(TreeUtils.toStreamInstanceOfMapper(Expression.class))
123+
.findFirst()
124+
.map(Expression::typeV2)
125+
.orElse(null);
126+
}
127+
128+
public static Stream<Tree> getSymbolNonDeclarationUsageTrees(SymbolV2 symbol) {
129+
return symbol.usages()
130+
.stream()
131+
// Function and class definition names will always have FunctionType and ClassType respectively
132+
// so they are filtered out of type propagation
133+
.filter(u -> !SymbolV2Utils.isDeclaration(u))
134+
.map(UsageV2::tree);
135+
}
136+
137+
private boolean isInSameScope(Propagation propagation, Name n, Tree scopeTree) {
138+
return Optional.ofNullable(propagation.scopeTree(n)).filter(scopeTree::equals).isPresent();
139+
}
140+
}
141+
73142
}

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

Lines changed: 0 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -17,21 +17,12 @@
1717
package org.sonar.python.semantic.v2.types;
1818

1919
import java.util.HashSet;
20-
import java.util.List;
21-
import java.util.Optional;
2220
import java.util.Set;
23-
import java.util.stream.Stream;
24-
import javax.annotation.CheckForNull;
25-
import org.sonar.plugins.python.api.tree.Expression;
2621
import org.sonar.plugins.python.api.tree.Name;
2722
import org.sonar.plugins.python.api.tree.Tree;
2823
import org.sonar.python.semantic.v2.SymbolV2;
29-
import org.sonar.python.semantic.v2.SymbolV2Utils;
30-
import org.sonar.python.semantic.v2.UsageV2;
31-
import org.sonar.python.tree.NameImpl;
3224
import org.sonar.python.tree.TreeUtils;
3325
import org.sonar.python.types.v2.PythonType;
34-
import org.sonar.python.types.v2.UnionType;
3526

3627
public abstract class Propagation {
3728

@@ -48,53 +39,11 @@ protected Propagation(SymbolV2 lhsSymbol, Name lhsName) {
4839
this.dependents = new HashSet<>();
4940
}
5041

51-
/**
52-
* This is used for AST-based type inference in try/catch statements
53-
* @return true if the propagation effectively changed the inferred type of assignment LHS
54-
*/
55-
boolean propagate(Set<SymbolV2> initializedVars) {
56-
PythonType rhsType = rhsType();
57-
Tree scopeTree = scopeTree(lhsName);
58-
if (initializedVars.add(lhsSymbol)) {
59-
getSymbolNonDeclarationUsageTrees(lhsSymbol)
60-
.filter(NameImpl.class::isInstance)
61-
.map(NameImpl.class::cast)
62-
// Avoid propagation to usages in nested scopes, as this may lead to FPs
63-
.filter(n -> isInSameScope(n, scopeTree))
64-
.forEach(n -> n.typeV2(rhsType));
65-
return true;
66-
} else {
67-
PythonType currentType = currentType(lhsName);
68-
if (currentType == null) {
69-
return false;
70-
}
71-
PythonType newType = UnionType.or(rhsType, currentType);
72-
getSymbolNonDeclarationUsageTrees(lhsSymbol)
73-
.filter(NameImpl.class::isInstance)
74-
.map(NameImpl.class::cast)
75-
.filter(n -> isInSameScope(n, scopeTree))
76-
.forEach(n -> n.typeV2(newType));
77-
return !newType.equals(currentType);
78-
}
79-
}
80-
81-
private boolean isInSameScope(Name n, Tree scopeTree) {
82-
return Optional.ofNullable(scopeTree(n)).filter(scopeTree::equals).isPresent();
83-
}
8442

8543
Tree scopeTree(Name name) {
8644
return TreeUtils.firstAncestor(name, t -> t.is(Tree.Kind.FUNCDEF, Tree.Kind.FILE_INPUT, Tree.Kind.CLASSDEF));
8745
}
8846

89-
public static Stream<Tree> getSymbolNonDeclarationUsageTrees(SymbolV2 symbol) {
90-
return symbol.usages()
91-
.stream()
92-
// Function and class definition names will always have FunctionType and ClassType respectively
93-
// so they are filtered out of type propagation
94-
.filter(u -> !SymbolV2Utils.isDeclaration(u))
95-
.map(UsageV2::tree);
96-
}
97-
9847
boolean areDependenciesReady(Set<SymbolV2> initializedVars) {
9948
return initializedVars.containsAll(variableDependencies);
10049
}
@@ -107,17 +56,6 @@ Set<Propagation> dependents() {
10756

10857
public abstract PythonType rhsType();
10958

110-
@CheckForNull
111-
static PythonType currentType(Name lhsName) {
112-
return Optional.ofNullable(lhsName.symbolV2())
113-
.stream()
114-
.flatMap(Propagation::getSymbolNonDeclarationUsageTrees)
115-
.flatMap(TreeUtils.toStreamInstanceOfMapper(Expression.class))
116-
.findFirst()
117-
.map(Expression::typeV2)
118-
.orElse(null);
119-
}
120-
12159
public SymbolV2 lhsSymbol() {
12260
return lhsSymbol;
12361
}

0 commit comments

Comments
 (0)