@Override
    public void visitReferenceExpression(@NotNull PsiReferenceExpression expression) {
      if (LOG.isDebugEnabled()) {
        LOG.debug("visitReferenceExpression " + expression);
      }
      PsiExpression qualifier = expression.getQualifierExpression();
      JavaResolveResult resolveResult = expression.advancedResolve(true);
      PsiElement element = resolveResult.getElement();

      if (element instanceof PsiLocalVariable || element instanceof PsiParameter) {
        final Value labeledValue = element.getUserData(CodeFragmentFactoryContextWrapper.LABEL_VARIABLE_VALUE_KEY);
        if (labeledValue != null) {
          myResult = new IdentityEvaluator(labeledValue);
          return;
        }
        final PsiVariable psiVar = (PsiVariable)element;
        String localName = psiVar.getName();
        //synthetic variable
        final PsiFile containingFile = element.getContainingFile();
        if (myCurrentFragmentEvaluator != null &&
            ((containingFile instanceof PsiCodeFragment && myVisitedFragments.contains(containingFile)) ||
             myCurrentFragmentEvaluator.hasValue(localName))) {
          // psiVariable may live in PsiCodeFragment not only in debugger editors, for example Fabrique has such variables.
          // So treat it as synthetic var only when this code fragment is located in DebuggerEditor,
          // that's why we need to check that containing code fragment is the one we visited
          JVMName jvmName = JVMNameUtil.getJVMQualifiedName(CompilingEvaluatorTypesUtil.getVariableType((PsiVariable)element));
          myResult = new SyntheticVariableEvaluator(myCurrentFragmentEvaluator, localName, jvmName);
          return;
        }
        // local variable
        PsiClass variableClass = getContainingClass(psiVar);
        final PsiClass positionClass = getPositionClass();
        if (Objects.equals(positionClass, variableClass)) {
          PsiElement method = DebuggerUtilsEx.getContainingMethod(expression);
          boolean canScanFrames = method instanceof PsiLambdaExpression || ContextUtil.isJspImplicit(element);
          myResult = new LocalVariableEvaluator(localName, canScanFrames);
          return;
        }
        // the expression references final var outside the context's class (in some of the outer classes)
        // oneLevelLess() because val$ are located in the same class
        CaptureTraverser traverser = createTraverser(variableClass, "Base class not found for " + psiVar.getName(), false).oneLevelLess();
        if (traverser.isValid()) {
          PsiExpression initializer = psiVar.getInitializer();
          if (initializer != null) {
            Object value = JavaPsiFacade.getInstance(psiVar.getProject()).getConstantEvaluationHelper().computeConstantExpression(initializer);
            if (value != null) {
              PsiType type = resolveResult.getSubstitutor().substitute(psiVar.getType());
              myResult = new LiteralEvaluator(value, type.getCanonicalText());
              return;
            }
          }
          Evaluator objectEvaluator = new ThisEvaluator(traverser);
          myResult = createFallbackEvaluator(
            new FieldEvaluator(objectEvaluator, FieldEvaluator.createClassFilter(positionClass), "val$" + localName),
            new LocalVariableEvaluator(localName, true));
          return;
        }
        throw evaluateException(JavaDebuggerBundle.message("evaluation.error.local.variable.missing.from.class.closure", localName));
      }
      else if (element instanceof PsiField psiField) {
        final PsiClass fieldClass = psiField.getContainingClass();
        if (fieldClass == null) {
          throw evaluateException(JavaDebuggerBundle.message("evaluation.error.cannot.resolve.field.class", psiField.getName()));
        }
        Evaluator objectEvaluator;
        if (psiField.hasModifierProperty(PsiModifier.STATIC)) {
          JVMName className = JVMNameUtil.getContextClassJVMQualifiedName(SourcePosition.createFromElement(psiField));
          if (className == null) {
            className = JVMNameUtil.getJVMQualifiedName(fieldClass);
          }
          objectEvaluator = new TypeEvaluator(className);
        }
        else if (qualifier != null) {
          qualifier.accept(this);
          objectEvaluator = myResult;
        }
        else {
          CaptureTraverser traverser = createTraverser(fieldClass, fieldClass.getName(), true);
          if (!traverser.isValid()) {
            throw evaluateException(JavaDebuggerBundle.message("evaluation.error.cannot.sources.for.field.class", psiField.getName()));
          }
          objectEvaluator = new ThisEvaluator(traverser);
        }
        myResult = new FieldEvaluator(objectEvaluator, FieldEvaluator.createClassFilter(fieldClass), psiField.getName());
      }
      else {
        //let's guess what this could be
        PsiElement nameElement = expression.getReferenceNameElement(); // get "b" part
        String name;
        if (nameElement instanceof PsiIdentifier) {
          name = nameElement.getText();
        }
        else {
          final String elementDisplayString = nameElement != null ? nameElement.getText() : "(null)";
          throw evaluateException(JavaDebuggerBundle.message("evaluation.error.identifier.expected", elementDisplayString));
        }

        if (qualifier != null) {
          final PsiElement qualifierTarget = qualifier instanceof PsiReferenceExpression
                                             ? ((PsiReferenceExpression)qualifier).resolve() : null;
          if (qualifierTarget instanceof PsiClass psiClass) {
            // this is a call to a 'static' field
            final JVMName typeName = JVMNameUtil.getJVMQualifiedName(psiClass);
            myResult = new FieldEvaluator(new TypeEvaluator(typeName), FieldEvaluator.createClassFilter(psiClass), name);
          }
          else {
            qualifier.accept(this);
            if (myResult == null) {
              throw evaluateException(JavaDebuggerBundle.message("evaluation.error.cannot.evaluate.qualifier", qualifier.getText()));
            }

            myResult = new FieldEvaluator(myResult, FieldEvaluator.createClassFilter(qualifier.getType()), name);
          }
        }
        else {
          myResult = createFallbackEvaluator(new LocalVariableEvaluator(name, false),
                                             new FieldEvaluator(new ThisEvaluator(), FieldEvaluator.TargetClassFilter.ALL, name));
        }
      }
    }
@Override
    public void visitReferenceExpression(@NotNull PsiReferenceExpression expression)