Skip to content

Commit

Permalink
SONARPY-1345 Fix stack overflow when a nested class inherits from a c…
Browse files Browse the repository at this point in the history
…lass with the same name (#1498)
  • Loading branch information
guillaume-dequenne authored Jun 21, 2023
1 parent 30aa413 commit 3a19fd5
Show file tree
Hide file tree
Showing 2 changed files with 27 additions and 4 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
import java.util.ArrayList;
import java.util.Collections;
import java.util.EnumSet;
import java.util.HashSet;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
Expand Down Expand Up @@ -145,21 +146,22 @@ public static ClassSymbol getClassSymbolFromDef(@Nullable ClassDef classDef) {
}

public static List<String> getParentClassesFQN(ClassDef classDef) {
return getParentClasses(TreeUtils.getClassSymbolFromDef(classDef)).stream()
return getParentClasses(TreeUtils.getClassSymbolFromDef(classDef), new HashSet<>()).stream()
.map(Symbol::fullyQualifiedName)
.filter(Objects::nonNull)
.collect(Collectors.toList());
}

private static List<Symbol> getParentClasses(@Nullable ClassSymbol classSymbol) {
private static List<Symbol> getParentClasses(@Nullable ClassSymbol classSymbol, Set<ClassSymbol> visitedSymbols) {
List<Symbol> superClasses = new ArrayList<>();
if (classSymbol == null) {
if (classSymbol == null || visitedSymbols.contains(classSymbol)) {
return superClasses;
}
visitedSymbols.add(classSymbol);
for (Symbol symbol : classSymbol.superClasses()) {
superClasses.add(symbol);
if (symbol instanceof ClassSymbol) {
superClasses.addAll(getParentClasses((ClassSymbol) symbol));
superClasses.addAll(getParentClasses((ClassSymbol) symbol, visitedSymbols));
}
}
return superClasses;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,15 +38,18 @@
import org.sonar.plugins.python.api.symbols.Symbol;
import org.sonar.plugins.python.api.symbols.Usage;
import org.sonar.plugins.python.api.tree.CallExpression;
import org.sonar.plugins.python.api.tree.ClassDef;
import org.sonar.plugins.python.api.tree.FileInput;
import org.sonar.plugins.python.api.tree.FunctionDef;
import org.sonar.plugins.python.api.tree.ImportFrom;
import org.sonar.plugins.python.api.tree.QualifiedExpression;
import org.sonar.plugins.python.api.tree.Statement;
import org.sonar.plugins.python.api.tree.Tree;
import org.sonar.python.PythonTestUtils;
import org.sonar.python.index.Descriptor;
import org.sonar.python.index.DescriptorUtils;
import org.sonar.python.index.VariableDescriptor;
import org.sonar.python.tree.TreeUtils;
import org.sonar.python.types.DeclaredType;
import org.sonar.python.types.InferredTypes;

Expand Down Expand Up @@ -697,7 +700,25 @@ public void class_having_itself_as_superclass_should_not_trigger_error() {
FileInput fileInput = parseWithoutSymbols("class A(A): pass");
Set<Symbol> globalSymbols = globalSymbols(fileInput, "mod");
ClassSymbol a = (ClassSymbol) globalSymbols.iterator().next();
// SONARPY-1350: The parent "A" is not yet defined at the time it is read, so this is actually not correct
assertThat(a.superClasses()).containsExactly(a);
ClassDef classDef = (ClassDef) fileInput.statements().statements().get(0);
assertThat(TreeUtils.getParentClassesFQN(classDef)).containsExactly("mod.mod.A");
}


@Test
public void class_having_another_class_with_same_name_should_not_trigger_error() {
FileInput fileInput = parseWithoutSymbols(
"from external import B",
"class A:",
" class B(B): pass"
);
globalSymbols(fileInput, "mod");
ClassDef outerClassDef = (ClassDef) fileInput.statements().statements().get(1);
ClassDef innerClassDef = (ClassDef) outerClassDef.body().statements().get(0);
// SONARPY-1350: Parent should be external.B
assertThat(TreeUtils.getParentClassesFQN(innerClassDef)).containsExactly("mod.mod.A.B");
}

@Test
Expand Down

0 comments on commit 3a19fd5

Please sign in to comment.