@Test
  public void canUseModuleInternalTypedefsInJsDoc() {
    CompilerUtil compiler = createCompiler(path("foo.js"));

    compiler.compile(
        createSourceFile(
            path("foo.js"),
            "/** @typedef {{x: number}} */",
            "var Variable;",
            "",
            "/**",
            " * @param {Variable} a .",
            " * @param {Variable} b .",
            " * @return {Variable} .",
            " */",
            "exports.add = function(a, b) {",
            "  return {x: a.x + b.x};",
            "};"));

    Scope scope = compiler.getCompiler().getTopScope();
    Var var = scope.getVar("module$foo");
    JSType type = var.getInitialValue().getJSType().toObjectType().getPropertyType("add");
    assertTrue(type.isFunctionType());

    JSDocInfo info = type.getJSDocInfo();
    Node node = info.getTypeNodes().iterator().next();
    assertTrue(node.isString());
    assertEquals("$jscomp.scope.Variable", node.getString());
    assertEquals("Variable", node.getProp(Node.ORIGINALNAME_PROP));
  }
  @Test
  public void savesOriginalTypeNameInJsDoc() {
    CompilerUtil compiler = createCompiler(path("foo.js"));

    compiler.compile(
        createSourceFile(
            path("foo.js"),
            "/** @constructor */",
            "var Builder = function(){};",
            "/** @return {!Builder} . */",
            "Builder.prototype.returnThis = function() { return this; };",
            "exports.Builder = Builder"));

    Scope scope = compiler.getCompiler().getTopScope();
    Var var = scope.getVar("module$foo");
    JSType type = var.getInitialValue().getJSType().findPropertyType("Builder");
    assertTrue(type.isConstructor());

    type = type.toObjectType().getTypeOfThis();
    assertEquals("$jscomp.scope.Builder", type.toString());

    type = type.toObjectType().getPropertyType("returnThis");
    assertTrue(type.toString(), type.isFunctionType());

    JSDocInfo info = type.getJSDocInfo();
    assertNotNull(info);

    Node node = getOnlyElement(info.getTypeNodes());
    assertEquals(Token.BANG, node.getType());

    node = node.getFirstChild();
    assertTrue(node.isString());
    assertEquals("$jscomp.scope.Builder", node.getString());
    assertEquals("Builder", node.getProp(Node.ORIGINALNAME_PROP));
  }
  @Test
  public void exportedInternalVarInheritsJsDocInfo() {
    CompilerUtil compiler = createCompiler(path("foo.js"));

    compiler.compile(
        createSourceFile(
            path("foo.js"),
            "/**",
            " * @constructor",
            " */",
            "var Greeter = function(){};",
            "/**",
            " * @param {string} name .",
            " * @return {string} .",
            " */",
            "Greeter.prototype.sayHi = function(name) {",
            "  return 'Hello, ' + name;",
            "};",
            "",
            "exports.Greeter = Greeter"));

    JSType exportedGreeter =
        compiler
            .getCompiler()
            .getTopScope()
            .getVar("module$foo")
            .getType()
            .findPropertyType("Greeter");
    assertTrue(exportedGreeter.isConstructor());
  }
  @Test
  public void canReferenceConstructorDefinedInTheGlobalScope() {
    CompilerUtil compiler = createCompiler(path("x/bar.js"));

    compiler.compile(
        createSourceFile(path("x/foo.js"), "/** @constructor */", "function Foo() {}"),
        createSourceFile(
            path("x/bar.js"), "/** @type {function(new: Foo)} */", "exports.Foo = Foo;"));

    Scope scope = compiler.getCompiler().getTopScope();
    Var var = scope.getVar("module$x$bar");

    JSType type = var.getInitialValue().getJSType().findPropertyType("Foo");
    assertTrue(type.isConstructor());

    type = type.toObjectType().getTypeOfThis();
    assertEquals("Foo", type.toString());
  }
  @Test
  public void canReferenceConstructorExportedByAnotherModule() {
    CompilerUtil compiler = createCompiler(path("x/foo.js"), path("x/bar.js"));

    compiler.compile(
        createSourceFile(path("x/foo.js"), "/** @constructor */", "exports.Foo = function(){};"),
        createSourceFile(
            path("x/bar.js"),
            "var foo = require('./foo');",
            "/** @type {function(new: foo.Foo)} */",
            "exports.Foo = foo.Foo;"));

    Scope scope = compiler.getCompiler().getTopScope();
    Var var = scope.getVar("module$x$bar");

    JSType type = var.getInitialValue().getJSType().findPropertyType("Foo");
    assertTrue(type.isConstructor());

    type = type.toObjectType().getTypeOfThis();
    assertEquals("module$x$foo.Foo", type.toString());
  }
  @Test
  public void canReferenceExportedTypeReferences() {
    CompilerUtil compiler = createCompiler(path("foo/bar.js"), path("foo/baz.js"));

    compiler.compile(
        createSourceFile(path("foo/bar.js"), "/** @constructor */", "exports.foo = function(){};"),
        createSourceFile(
            path("foo/baz.js"),
            "var bar = require('./bar');",
            "var foo = require('./bar').foo;",
            "/** @type {!bar.foo} */",
            "var a = new bar.foo();",
            "/** @type {!foo} */",
            "var b = new foo();"));

    assertThat(compiler.toSource())
        .startsWith(
            lines(
                "var $jscomp = {};",
                "$jscomp.scope = {};",
                "var module$foo$bar = {};",
                "module$foo$bar.foo = function() {",
                "};",
                "var module$foo$baz = {};",
                "$jscomp.scope.a = new module$foo$bar.foo;",
                "$jscomp.scope.b = new module$foo$bar.foo;"));

    ObjectType scope =
        compiler
            .getCompiler()
            .getTopScope()
            .getVar("$jscomp")
            .getType()
            .findPropertyType("scope")
            .toObjectType();

    assertEquals("module$foo$bar.foo", scope.getPropertyType("a").getDisplayName());
    assertEquals("module$foo$bar.foo", scope.getPropertyType("b").getDisplayName());
  }