Repository: nodejs/node
File: lib/internal/repl/completion.js (JavaScript)

Code before:
        }

        return evalFn(
          `try { ${exprStr} } catch {} `, ctx, getREPLResourceName(), (err, obj) => {
            if (err) {
              return callback(false);
            }
            return callback(false, obj);
          });
      });
    });
  }

  /**
   * Utility to see if a property has a getter associated to it or if
   * the property itself is a proxy object.
   * @returns {void}
   */
  function hasGetterOrIsProxy(obj, astProp, cb) {
    if (!obj || !astProp) {
      return cb(false);
    }

    if (astProp.type === 'Literal') {
      // We have something like `obj['foo'].x` where `x` is the literal
      return propHasGetterOrIsProxy(obj, `${astProp.value}`, cb);
    }

    if (
      astProp.type === 'Identifier' &&
      exprStr.at(astProp.start - 1) === '.'
    ) {
      // We have something like `obj.foo.x` where `foo` is the identifier
      return propHasGetterOrIsProxy(obj, `${astProp.name}`, cb);
    }

    return evalFn(
      // Note: this eval runs the property expression, which might be side-effectful, for example
      //       the user could be running `obj[getKey()].` where `getKey()` has some side effects.
      //       Arguably this behavior should not be too surprising, but if it turns out that it is,
      //       then we can revisit this behavior and add logic to analyze the property expression
      //       and eval it only if we can confidently say that it can't have any side effects
      `try { ${exprStr.slice(astProp.start, astProp.end)} } catch {} `,
      ctx,
      getREPLResourceName(),
      (err, evaledProp) => {
        if (err) {
          return cb(false);
        }

        if (typeof evaledProp === 'string') {

Review comment (suggestion):
Should this not just be

```suggestion
      return propHasGetterOrIsProxy(obj, astProp.value, cb);
```

Code after:
        }

        return evalFn(
          `try { ${exprStr} } catch {} `, ctx, getREPLResourceName(), (err, obj) => {
            if (err) {
              return callback(false);
            }
            return callback(false, obj);
          });
      });
    });
  }

  /**
   * Utility to see if a property has a getter associated to it or if
   * the property itself is a proxy object.
   * @returns {void}
   */
  function hasGetterOrIsProxy(obj, astProp, cb) {
    if (!obj || !astProp) {
      return cb(false);
    }

    if (astProp.type === 'Literal') {
      // We have something like `obj['foo'].x` where `x` is the literal
      return propHasGetterOrIsProxy(obj, astProp.value, cb);
    }

    if (
      astProp.type === 'Identifier' &&
      exprStr.at(astProp.start - 1) === '.'
    ) {
      // We have something like `obj.foo.x` where `foo` is the identifier
      return propHasGetterOrIsProxy(obj, astProp.name, cb);
    }

    return evalFn(
      // Note: this eval runs the property expression, which might be side-effectful, for example
      //       the user could be running `obj[getKey()].` where `getKey()` has some side effects.
      //       Arguably this behavior should not be too surprising, but if it turns out that it is,
      //       then we can revisit this behavior and add logic to analyze the property expression
      //       and eval it only if we can confidently say that it can't have any side effects
      `try { ${exprStr.slice(astProp.start, astProp.end)} } catch {} `,
      ctx,
      getREPLResourceName(),
      (err, evaledProp) => {
        if (err) {
          return cb(false);
        }

        if (typeof evaledProp === 'string') {