code: static String getGlobalNameForJsDoc(
        ModuleMap moduleMap, Binding binding, List<String> propertyChain) {
      int prop = 0;
  
      while (binding.isModuleNamespace()
          && binding.metadata().isEs6Module()
          && prop < propertyChain.size()) {
        String propertyName = propertyChain.get(prop);
        Module m = moduleMap.getModule(binding.metadata().path());
        if (m.namespace().containsKey(propertyName)) {
          binding = m.namespace().get(propertyName);
        } else {
          // This means someone referenced an invalid export on a module object. This should be an
          // error, so just rewrite and let the type checker complain later. It isn't a super clear
          // error, but we're working on type checking modules soon.
          break;
        }
        prop++;
      }
  
      String globalName = getGlobalName(binding);
  
      if (prop < propertyChain.size()) {
        globalName =
            globalName + "." + Joiner.on('.').join(propertyChain.subList(prop, propertyChain.size()));
      }
  
      return globalName;
    }
docs: Returns the globalized name of a reference to a binding in JS Doc. See {@link
  #replace(AbstractCompiler, ModuleMap, Binding, Node)} to replace actual code nodes.
  
  <p>For example:
  
  <pre>
  // bar
  export class Bar {}
  </pre>
  
  <pre>
  // foo
  import * as bar from 'bar';
  export {bar};
  </pre>
  
  <pre>
  import * as foo from 'foo';
  let /** !foo.bar.Bar *\/ b;
  </pre>
  
  <p>Should call this method with the binding for {@code foo} and a list ("bar", "Bar"). In this
  example any of these properties could also be modules. This method will replace as much as the
  GETPROP as it can with module exported variables. Meaning in the above example this would
  return something like "baz$$module$bar", whereas if this method were called for just "foo.bar"
  it would return "module$bar", as it refers to a module object itself.