@Override
    public void visitPyReferenceExpression(PyReferenceExpression node) {
      final PyExpression qualifier = node.getQualifier();
      if (qualifier == null || PyNames.CANONICAL_SELF.equals(qualifier.getText())) return;
      if (myTypeEvalContext.getType(qualifier) instanceof PyNamedTupleType) return;
      final String name = node.getName();
      final List<LocalQuickFix> quickFixes = new ArrayList<LocalQuickFix>();
      quickFixes.add(new PyRenameElementQuickFix());

      if (name != null && name.startsWith("_") && !name.startsWith("__") && !name.endsWith("__")) {
        final PsiReference reference = node.getReference(getResolveContext());
        if (reference == null) return;
        final PsiElement resolvedExpression = reference.resolve();
        final PyClass resolvedClass = getClassOwner(resolvedExpression);
        if (resolvedExpression instanceof PyTargetExpression) {
          final String newName = StringUtil.trimLeading(name, '_');
          if (resolvedClass != null) {
            final String qFixName =
                resolvedClass.getProperties().containsKey(newName)
                    ? PyBundle.message("QFIX.use.property")
                    : PyBundle.message("QFIX.add.property");
            quickFixes.add(new PyAddPropertyForFieldQuickFix(qFixName));

            final Collection<String> usedNames = PyRefactoringUtil.collectUsedNames(resolvedClass);
            if (!usedNames.contains(newName)) {
              quickFixes.add(new PyMakePublicQuickFix());
            }
          }
        }

        final PyClass parentClass = getClassOwner(node);
        if (parentClass != null) {
          if (PyTestUtil.isPyTestClass(parentClass) && ignoreTestFunctions) return;

          if (parentClass.isSubclass(resolvedClass)) return;

          PyClass outerClass = getClassOwner(parentClass);
          while (outerClass != null) {
            if (outerClass.isSubclass(resolvedClass)) return;

            outerClass = getClassOwner(outerClass);
          }
        }
        final PyType type = myTypeEvalContext.getType(qualifier);
        final String bundleKey =
            type instanceof PyModuleType
                ? "INSP.protected.member.$0.access.module"
                : "INSP.protected.member.$0.access";
        registerProblem(
            node,
            PyBundle.message(bundleKey, name),
            ProblemHighlightType.GENERIC_ERROR_OR_WARNING,
            null,
            quickFixes.toArray(new LocalQuickFix[quickFixes.size() - 1]));
      }
    }
 @NotNull
 public QualifiedResolveResult followAssignmentsChain(PyResolveContext resolveContext) {
   PyReferenceExpression seeker = this;
   QualifiedResolveResult ret = null;
   List<PyExpression> qualifiers = new ArrayList<PyExpression>();
   PyExpression qualifier = seeker.getQualifier();
   if (qualifier != null) {
     qualifiers.add(qualifier);
   }
   Set<PsiElement> visited = new HashSet<PsiElement>();
   visited.add(this);
   SEARCH:
   while (ret == null) {
     ResolveResult[] targets = seeker.getReference(resolveContext).multiResolve(false);
     for (ResolveResult target : targets) {
       PsiElement elt = target.getElement();
       if (elt instanceof PyTargetExpression) {
         PsiElement assigned_from = null;
         final PyTargetExpression expr = (PyTargetExpression) elt;
         final TypeEvalContext context = resolveContext.getTypeEvalContext();
         if (context.maySwitchToAST(expr) || expr.getStub() == null) {
           assigned_from = expr.findAssignedValue();
         }
         // TODO: Maybe findAssignedValueByStub() should become a part of the PyTargetExpression
         // interface
         else if (elt instanceof PyTargetExpressionImpl) {
           assigned_from = ((PyTargetExpressionImpl) elt).findAssignedValueByStub(context);
         }
         if (assigned_from instanceof PyReferenceExpression) {
           if (visited.contains(assigned_from)) {
             break;
           }
           visited.add(assigned_from);
           seeker = (PyReferenceExpression) assigned_from;
           if (seeker.getQualifier() != null) {
             qualifiers.add(seeker.getQualifier());
           }
           continue SEARCH;
         } else if (assigned_from != null)
           ret = new QualifiedResolveResultImpl(assigned_from, qualifiers, false);
       } else if (ret == null && elt instanceof PyElement && target.isValidResult()) {
         // remember this result, but a further reference may be the next resolve result
         ret =
             new QualifiedResolveResultImpl(
                 elt, qualifiers, target instanceof ImplicitResolveResult);
       }
     }
     // all resolve results checked, reassignment not detected, nothing more to do
     break;
   }
   if (ret == null) ret = EMPTY_RESULT;
   return ret;
 }
 @Override
 public void visitPyReferenceExpression(PyReferenceExpression node) {
   super.visitPyReferenceExpression(node);
   final PsiElement result = node.getReference().resolve();
   if (result != null && PsiTreeUtil.getParentOfType(result, ScopeOwner.class) == myScope) {
     if (result instanceof PyParameter && myScope instanceof PyFunction) {
       final PyFunction function = (PyFunction) myScope;
       final PyParameter[] parameters = function.getParameterList().getParameters();
       if (parameters.length > 0 && result == parameters[0]) {
         final PyFunction.Modifier modifier = function.getModifier();
         if (modifier != PyFunction.Modifier.STATICMETHOD) {
           // 'self' is not a local scope dependency
           return;
         }
       }
     }
     hasLocalScopeDependencies = true;
   }
 }
 @NotNull
 private static List<PsiElement> tryResolving(
     @NotNull PyExpression expression, @NotNull TypeEvalContext context) {
   final List<PsiElement> elements = Lists.newArrayList();
   if (expression instanceof PyReferenceExpression) {
     final PyReferenceExpression referenceExpr = (PyReferenceExpression) expression;
     final PyResolveContext resolveContext =
         PyResolveContext.noImplicits().withTypeEvalContext(context);
     final PsiPolyVariantReference reference = referenceExpr.getReference(resolveContext);
     final List<PsiElement> resolved = PyUtil.multiResolveTopPriority(reference);
     for (PsiElement element : resolved) {
       if (element instanceof PyFunction) {
         final PyFunction function = (PyFunction) element;
         if (PyUtil.isInit(function)) {
           final PyClass cls = function.getContainingClass();
           if (cls != null) {
             elements.add(cls);
             continue;
           }
         }
       } else if (element instanceof PyTargetExpression) {
         final PyTargetExpression targetExpr = (PyTargetExpression) element;
         // XXX: Requires switching from stub to AST
         final PyExpression assignedValue = targetExpr.findAssignedValue();
         if (assignedValue != null) {
           elements.add(assignedValue);
           continue;
         }
       }
       if (element != null) {
         elements.add(element);
       }
     }
   }
   return !elements.isEmpty() ? elements : Collections.singletonList(expression);
 }
 @Override
 public void visitPyReferenceExpression(final PyReferenceExpression node) {
   if (node.getContainingFile() instanceof PyExpressionCodeFragment) {
     return;
   }
   // Ignore global statements arguments
   if (PyGlobalStatementNavigator.getByArgument(node) != null) {
     return;
   }
   // Ignore qualifier inspections
   if (node.getQualifier() != null) {
     return;
   }
   // Ignore import subelements
   if (PsiTreeUtil.getParentOfType(node, PyImportStatementBase.class) != null) {
     return;
   }
   final String name = node.getReferencedName();
   if (name == null) {
     return;
   }
   final ScopeOwner owner = ScopeUtil.getDeclarationScopeOwner(node, name);
   final Set<ScopeOwner> largeFunctions = getSession().getUserData(LARGE_FUNCTIONS_KEY);
   assert largeFunctions != null;
   if (owner == null || largeFunctions.contains(owner)) {
     return;
   }
   // Ignore references declared in outer scopes
   if (owner != ScopeUtil.getScopeOwner(node)) {
     return;
   }
   final Scope scope = ControlFlowCache.getScope(owner);
   // Ignore globals and if scope even doesn't contain such a declaration
   if (scope.isGlobal(name) || (!scope.containsDeclaration(name))) {
     return;
   }
   // Start DFA from the assignment statement in case of augmented assignments
   final PsiElement anchor;
   final PyAugAssignmentStatement augAssignment =
       PsiTreeUtil.getParentOfType(node, PyAugAssignmentStatement.class);
   if (augAssignment != null && name.equals(augAssignment.getTarget().getName())) {
     anchor = augAssignment;
   } else {
     anchor = node;
   }
   final ScopeVariable variable;
   try {
     variable = scope.getDeclaredVariable(anchor, name);
   } catch (DFALimitExceededException e) {
     largeFunctions.add(owner);
     registerLargeFunction(owner);
     return;
   }
   if (variable == null) {
     if (!isFirstUnboundRead(node, owner)) {
       return;
     }
     final PsiPolyVariantReference ref = node.getReference(resolveWithoutImplicits());
     if (ref == null) {
       return;
     }
     final PsiElement resolved = ref.resolve();
     final boolean isBuiltin = PyBuiltinCache.getInstance(node).hasInBuiltins(resolved);
     if (owner instanceof PyClass) {
       if (isBuiltin || ScopeUtil.getDeclarationScopeOwner(owner, name) != null) {
         return;
       }
     }
     if (PyUnreachableCodeInspection.hasAnyInterruptedControlFlowPaths(node)) {
       return;
     }
     if (owner instanceof PyFile) {
       if (isBuiltin) {
         return;
       }
       if (resolved != null && !PyUtil.inSameFile(node, resolved)) {
         return;
       }
       registerProblem(node, PyBundle.message("INSP.unbound.name.not.defined", name));
     } else {
       registerProblem(
           node,
           PyBundle.message("INSP.unbound.local.variable", node.getName()),
           ProblemHighlightType.GENERIC_ERROR_OR_WARNING,
           null,
           new AddGlobalQuickFix());
     }
   }
 }