Skip to content

Commit b33dd7f

Browse files
Fix remaining StackOverflowError condition (#1039)
1 parent e45d063 commit b33dd7f

File tree

3 files changed

+42
-20
lines changed

3 files changed

+42
-20
lines changed

python-frontend/src/main/java/org/sonar/python/index/DescriptorUtils.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -167,7 +167,7 @@ private static void addSuperClasses(ClassSymbolImpl classSymbol, ClassDescriptor
167167
if (createdSymbols.containsKey(superClassFqn)) {
168168
return createdSymbols.get(superClassFqn);
169169
}
170-
Symbol symbol = projectLevelSymbolTable.getSymbol(superClassFqn);
170+
Symbol symbol = projectLevelSymbolTable.getSymbol(superClassFqn, null, createdSymbols);
171171
symbol = symbol != null ? symbol : typeshedSymbolWithFQN(superClassFqn);
172172
createdSymbols.put(superClassFqn, symbol);
173173
return symbol;
@@ -188,7 +188,7 @@ private static void addParameters(FunctionSymbolImpl functionSymbol, FunctionDes
188188
functionDescriptor.parameters().stream().map(p -> {
189189
FunctionSymbolImpl.ParameterImpl parameter = new FunctionSymbolImpl.ParameterImpl(p);
190190
Symbol existingSymbol = createdSymbols.get(p.annotatedType());
191-
Symbol typeSymbol = existingSymbol != null ? existingSymbol : projectLevelSymbolTable.getSymbol(p.annotatedType());
191+
Symbol typeSymbol = existingSymbol != null ? existingSymbol : projectLevelSymbolTable.getSymbol(p.annotatedType(), null, createdSymbols);
192192
String annotatedTypeName = parameter.annotatedTypeName();
193193
if (typeSymbol == null && annotatedTypeName != null) {
194194
typeSymbol = typeshedSymbolWithFQN(annotatedTypeName);

python-frontend/src/main/java/org/sonar/python/semantic/ProjectLevelSymbolTable.java

Lines changed: 5 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,6 @@ public class ProjectLevelSymbolTable {
5050
private final Map<String, Set<Descriptor>> globalDescriptorsByModuleName;
5151
private Map<String, Descriptor> globalDescriptorsByFQN;
5252
private final Set<String> djangoViewsFQN = new HashSet<>();
53-
private Set<String> queriedSymbolNames = new HashSet<>();
5453

5554
public static ProjectLevelSymbolTable empty() {
5655
return new ProjectLevelSymbolTable(Collections.emptyMap());
@@ -138,24 +137,13 @@ public Symbol getSymbol(@Nullable String fullyQualifiedName) {
138137
}
139138

140139
public Symbol getSymbol(@Nullable String fullyQualifiedName, @Nullable String localSymbolName) {
140+
return getSymbol(fullyQualifiedName, localSymbolName, new HashMap<>());
141+
}
142+
143+
public Symbol getSymbol(@Nullable String fullyQualifiedName, @Nullable String localSymbolName, Map<String, Symbol> createdSymbols) {
141144
if (fullyQualifiedName == null) return null;
142-
if (queriedSymbolNames.contains(fullyQualifiedName)) {
143-
// cyclic dependencies
144-
queriedSymbolNames = new HashSet<>();
145-
String[] fqnSplitByDot = fullyQualifiedName.split("\\.");
146-
localSymbolName = localSymbolName != null ? localSymbolName : fqnSplitByDot[fqnSplitByDot.length - 1];
147-
return new SymbolImpl(localSymbolName, fullyQualifiedName);
148-
}
149145
Descriptor descriptor = globalDescriptorsByFQN().get(fullyQualifiedName);
150-
if (descriptor == null) {
151-
queriedSymbolNames = new HashSet<>();
152-
return null;
153-
} else {
154-
queriedSymbolNames.add(fullyQualifiedName);
155-
Symbol symbol = DescriptorUtils.symbolFromDescriptor(descriptor, this, localSymbolName, new HashMap<>());
156-
queriedSymbolNames = new HashSet<>();
157-
return symbol;
158-
}
146+
return descriptor == null ? null : DescriptorUtils.symbolFromDescriptor(descriptor, this, localSymbolName, createdSymbols);
159147
}
160148

161149
@CheckForNull

python-frontend/src/test/java/org/sonar/python/semantic/ProjectLevelSymbolTableTest.java

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -683,7 +683,7 @@ public void loop_in_class_inheritance() {
683683
ClassSymbolImpl parentOfImportedA = (ClassSymbolImpl) importedA.superClasses().iterator().next();
684684
assertThat(parentOfImportedA.superClasses())
685685
.extracting(Symbol::kind, Symbol::fullyQualifiedName)
686-
.containsExactly(tuple(Symbol.Kind.OTHER, "foo.A"));
686+
.containsExactly(tuple(Symbol.Kind.CLASS, "foo.A"));
687687
}
688688

689689
@Test
@@ -741,6 +741,40 @@ public void class_with_method_parameter_of_same_type() {
741741
assertThat(declaredType.getTypeClass()).isSameAs(classSymbol);
742742
}
743743

744+
@Test
745+
public void loop_in_inheritance_with_method_paraneters_of_same_type() {
746+
String[] foo = {
747+
"from bar import B",
748+
"class A(B):",
749+
" def my_A_method(param: A): ...",
750+
" def my_A_other_method(param: B): ..."
751+
};
752+
String[] bar = {
753+
"from foo import A",
754+
"class B(A):",
755+
" def my_B_method(param: A): ...",
756+
" def my_B_other_method(param: B): ..."
757+
};
758+
759+
ProjectLevelSymbolTable projectSymbolTable = new ProjectLevelSymbolTable();
760+
projectSymbolTable.addModule(parseWithoutSymbols(foo), "", pythonFile("foo.py"));
761+
projectSymbolTable.addModule(parseWithoutSymbols(bar), "", pythonFile("bar.py"));
762+
763+
Set<Symbol> fooSymbols = projectSymbolTable.getSymbolsFromModule("foo");
764+
ClassSymbol classSymbolA = (ClassSymbol) fooSymbols.stream().filter(s -> s.fullyQualifiedName().equals("foo.A")).findFirst().get();
765+
ClassSymbol classSymbolB = (ClassSymbol) classSymbolA.superClasses().get(0);
766+
767+
assertThat(classSymbolB.superClasses()).containsExactly(classSymbolA);
768+
assertThat(classSymbolA.declaredMembers().stream()
769+
.map(FunctionSymbol.class::cast)
770+
.flatMap(f -> f.parameters().stream()
771+
.map(FunctionSymbol.Parameter::declaredType)
772+
.map(DeclaredType.class::cast))
773+
.collect(Collectors.toList()))
774+
.extracting(DeclaredType::getTypeClass)
775+
.containsExactlyInAnyOrder(classSymbolA, classSymbolB);
776+
}
777+
744778
@Test
745779
public void django_views() {
746780
String[] urls = {

0 commit comments

Comments
 (0)