function isConstruction(node, callScope) {
  switch (node.type) {
    case 'Literal':
      if (node.regex != null) {
        return { type: 'regular expression', node };
      }
      return null;
    case 'Identifier': {
      const variableScoping = callScope.set.get(node.name);

      if (variableScoping == null || variableScoping.defs == null) {
        // If it's not in scope, we don't care.
        return null; // Handled
      }

      // Gets the last variable identity
      const variableDefs = variableScoping.defs;
      const def = variableDefs[variableDefs.length - 1];
      if (def != null
        && def.type !== 'Variable'
        && def.type !== 'FunctionName'
      ) {
        // Parameter or an unusual pattern. Bail out.
        return null; // Unhandled
      }

      if (def.node.type === 'FunctionDeclaration') {
        return { type: 'function declaration', node: def.node, usage: node };
      }

      const init = def.node.init;
      if (init == null) {
        return null;
      }

      const initConstruction = isConstruction(init, callScope);
      if (initConstruction == null) {
        return null;
      }

      return {
        type: initConstruction.type,
        node: initConstruction.node,
        usage: node,
      };
    }
    case 'ObjectExpression':
      // Any object initialized inline will create a new identity
      return { type: 'object', node };
    case 'ArrayExpression':
      return { type: 'array', node };
    case 'ArrowFunctionExpression':
    case 'FunctionExpression':
      // Functions that are initialized inline will have a new identity
      return { type: 'function expression', node };
    case 'ClassExpression':
      return { type: 'class expression', node };
    case 'NewExpression':
      // `const a = new SomeClass();` is a construction
      return { type: 'new expression', node };
    case 'ConditionalExpression':
      return (isConstruction(node.consequent, callScope)
        || isConstruction(node.alternate, callScope)
      );
    case 'LogicalExpression':
      return (isConstruction(node.left, callScope)
        || isConstruction(node.right, callScope)
      );
    case 'MemberExpression': {
      const objConstruction = isConstruction(node.object, callScope);
      if (objConstruction == null) {
        return null;
      }
      return {
        type: objConstruction.type,
        node: objConstruction.node,
        usage: node.object,
      };
    }
    case 'JSXFragment':
      return { type: 'JSX fragment', node };
    case 'JSXElement':
      return { type: 'JSX element', node };
    case 'AssignmentExpression': {
      const construct = isConstruction(node.right, callScope);
      if (construct != null) {
        return {
          type: 'assignment expression',
          node: construct.node,
          usage: node,
        };
      }
      return null;
    }
    case 'TypeCastExpression':
    case 'TSAsExpression':
      return isConstruction(node.expression, callScope);
    default:
      return null;
  }
}
@fileoverview Prevents jsx context provider values from taking values that
              will cause needless rerenders.
@author Dylan Oshima