<commit_before>package com.jetbrains.python.inspections;

import com.intellij.codeInspection.ProblemsHolder;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiElementVisitor;
import com.intellij.util.containers.Stack;
import com.jetbrains.python.PyBundle;
import com.jetbrains.python.PyNames;
import com.jetbrains.python.psi.*;
import com.jetbrains.python.psi.resolve.PyResolveContext;
import org.jetbrains.annotations.Nls;
import org.jetbrains.annotations.NotNull;

/**
 * User: catherine
 *
 * Inspection to warn if call to super constructor in class is missed
 */
public class PyMissingConstructorInspection extends PyInspection {
  @Nls
  @NotNull
  @Override
  public String getDisplayName() {
    return PyBundle.message("INSP.NAME.missing.super.constructor");
  }

  @NotNull
  @Override
  public PsiElementVisitor buildVisitor(@NotNull ProblemsHolder holder, boolean isOnTheFly) {
    return new Visitor(holder);
  }

  private static class Visitor extends PyInspectionVisitor {

    public Visitor(final ProblemsHolder holder) {
      super(holder);
    }

    @Override
    public void visitPyClass(final PyClass node) {
      PsiElement[] superClasses = node.getSuperClassExpressions();
      if (superClasses.length == 0 || (superClasses.length == 1 && PyNames.OBJECT.equals(superClasses[0].getText())))
        return;

      Stack<String> superNames = new Stack<String>();
      if (node.isNewStyleClass())
        superNames.push(PyNames.SUPER);

      addSuperNames(superNames, superClasses);

      if (!superHasConstructor(node)) return;
      PyFunction initMethod = node.findMethodByName(PyNames.INIT, false);
      if (initMethod != null) {
        if (hasConstructorCall(initMethod, superNames))
          return;
        registerProblem(initMethod.getNameIdentifier(), "Call to constructor of super class is missed");
      }
    }

    private static boolean superHasConstructor(PyClass node) {
      Stack<PyClass> st = new Stack<PyClass>();
      addSuperClasses(st, node.getSuperClasses());

      while (!st.empty()) {
        if ((st.pop()).findMethodByName(PyNames.INIT, false) != null) {
          return true;
        }
      }
      return false;
    }
    private static void addSuperClasses(Stack<PyClass> st, PyClass[] superClasses) {
      for (PyClass cl : superClasses) {
        st.push(cl);
        addSuperClasses(st, cl.getSuperClasses());
      }
    }

    private static void addSuperNames(Stack<String> st, PsiElement[] superClasses) {
      for (PsiElement cl : superClasses) {
        if (!PyNames.OBJECT.equals(cl.getText()))
          st.push(cl.getText());

        if (cl instanceof PyReferenceExpression) {
          PyReferenceExpression ref = (PyReferenceExpression) cl;
          final PsiElement result = ref.getReference(PyResolveContext.noProperties()).resolve();
          if (result instanceof PyClass)
            addSuperNames(st, ((PyClass)result).getSuperClassExpressions());
        }
      }
    }

    private static boolean hasConstructorCall(PyFunction initMethod, Stack<String> superNames) {
      Stack<PsiElement> stack = new Stack<PsiElement>();
      PyStatementList statementList = initMethod.getStatementList();
      boolean hasConstructor = false;
      if (statementList != null) {
        for (PyStatement st : statementList.getStatements()) {
          stack.push(st);
          while (!stack.isEmpty()) {
            PsiElement e = stack.pop();
            if (e instanceof PyExpressionStatement) {
              PyExpression expression = ((PyExpressionStatement)e).getExpression();
              if (expression instanceof PyCallExpression) {
                if (isConstructorCall((PyCallExpression)expression, superNames))
                  hasConstructor = true;
              }
            }<commit_msg>fixed PY-3395 Stack overflow if a class is inherited from itself<commit_after>package com.jetbrains.python.inspections;

import com.intellij.codeInspection.ProblemsHolder;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiElementVisitor;
import com.intellij.util.containers.Stack;
import com.jetbrains.python.PyBundle;
import com.jetbrains.python.PyNames;
import com.jetbrains.python.psi.*;
import com.jetbrains.python.psi.resolve.PyResolveContext;
import org.jetbrains.annotations.Nls;
import org.jetbrains.annotations.NotNull;

/**
 * User: catherine
 *
 * Inspection to warn if call to super constructor in class is missed
 */
public class PyMissingConstructorInspection extends PyInspection {
  @Nls
  @NotNull
  @Override
  public String getDisplayName() {
    return PyBundle.message("INSP.NAME.missing.super.constructor");
  }

  @NotNull
  @Override
  public PsiElementVisitor buildVisitor(@NotNull ProblemsHolder holder, boolean isOnTheFly) {
    return new Visitor(holder);
  }

  private static class Visitor extends PyInspectionVisitor {

    public Visitor(final ProblemsHolder holder) {
      super(holder);
    }

    @Override
    public void visitPyClass(final PyClass node) {
      PsiElement[] superClasses = node.getSuperClassExpressions();
      String name = node.getName();
      if (superClasses.length == 0 || (superClasses.length == 1 && PyNames.OBJECT.equals(superClasses[0].getText())))
        return;

      Stack<String> superNames = new Stack<String>();
      if (node.isNewStyleClass())
        superNames.push(PyNames.SUPER);

      addSuperNames(superNames, superClasses, name);

      if (!superHasConstructor(node)) return;
      PyFunction initMethod = node.findMethodByName(PyNames.INIT, false);
      if (initMethod != null) {
        if (hasConstructorCall(initMethod, superNames))
          return;
        registerProblem(initMethod.getNameIdentifier(), "Call to constructor of super class is missed");
      }
    }

    private static boolean superHasConstructor(PyClass node) {
      Stack<PyClass> st = new Stack<PyClass>();
      addSuperClasses(st, node.getSuperClasses(), node.getName());

      while (!st.empty()) {
        if ((st.pop()).findMethodByName(PyNames.INIT, false) != null) {
          return true;
        }
      }
      return false;
    }
    private static void addSuperClasses(Stack<PyClass> st, PyClass[] superClasses, String name) {
      for (PyClass cl : superClasses) {
        if (!name.equals(cl.getName())) {
          st.push(cl);
          addSuperClasses(st, cl.getSuperClasses(), name);
        }
      }
    }

    private static void addSuperNames(Stack<String> st, PsiElement[] superClasses, String name) {
      for (PsiElement cl : superClasses) {
        if (!name.equals(cl.getText())) {
          if (!PyNames.OBJECT.equals(cl.getText()))
            st.push(cl.getText());

          if (cl instanceof PyReferenceExpression) {
            PyReferenceExpression ref = (PyReferenceExpression) cl;
            final PsiElement result = ref.getReference(PyResolveContext.noProperties()).resolve();
            if (result instanceof PyClass)
              addSuperNames(st, ((PyClass)result).getSuperClassExpressions(), name);
          }
        }
      }
    }

    private static boolean hasConstructorCall(PyFunction initMethod, Stack<String> superNames) {
      Stack<PsiElement> stack = new Stack<PsiElement>();
      PyStatementList statementList = initMethod.getStatementList();
      boolean hasConstructor = false;
      if (statementList != null) {
        for (PyStatement st : statementList.getStatements()) {
          stack.push(st);
          while (!stack.isEmpty()) {
            PsiElement e = stack.pop();
            if (e instanceof PyExpressionStatement) {
              PyExpression expression = ((PyExpressionStatement)e).getExpression();
              if (expression instanceof PyCallExpression) {
                if (isConstructorCall((PyCallExpression)expression, superNames))
                  hasConstructor = true;
              }
            }
{'fraction_non_alphanumeric': 0.10575092040116796, 'fraction_numerical': 0.0012695188523549575, 'mean_word_length': 3.1332633788037776, 'pattern_counts': {'":': 0, '<': 21, '<?xml version=': 0, '>': 21, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6746834', 'n_tokens_mistral': 2160, 'n_tokens_neox': 2098, 'n_words': 491}