diff --git a/src/main/java/com/google/javascript/clutz/DeclarationGenerator.java b/src/main/java/com/google/javascript/clutz/DeclarationGenerator.java index ebe64a04..aafcf137 100644 --- a/src/main/java/com/google/javascript/clutz/DeclarationGenerator.java +++ b/src/main/java/com/google/javascript/clutz/DeclarationGenerator.java @@ -724,8 +724,7 @@ private void processUnprovidedTypes(Set provides, Set transitive } // skip provided symbols (as default or in an namespace). - if (provides.contains(name) - || (!transitiveProvides.contains(name) && provides.contains(namespace))) { + if (isProvidedSymbol(provides, transitiveProvides, name)) { continue; } @@ -764,6 +763,35 @@ private void processUnprovidedTypes(Set provides, Set transitive } } + /** Returns true if the symbol is already provided as default or in a namespace. */ + private boolean isProvidedSymbol( + Set provides, Set transitiveProvides, String name) { + if (provides.contains(name)) { + return true; + } + if (transitiveProvides.contains(name)) { + return false; + } + String namespace = getNamespace(name); + Boolean innerClass = false; + while (!namespace.isEmpty()) { + final TypedVar symbol = this.compiler.getTopScope().getOwnSlot(namespace); + if (symbol == null) { + return false; + } + // inner class-like of default export is emitted recursively + if (provides.contains(namespace) && (!innerClass || isDefaultExport(symbol))) { + return true; + } + if (!isClassLike(symbol.getType())) { + return false; + } + namespace = getNamespace(namespace); + innerClass = true; + } + return false; + } + /** * If any inputs declare a legacy namespace, emit aliases for their exports in goog.module style. */ diff --git a/src/test/java/com/google/javascript/clutz/double_inner_class_with_referenced_props.d.ts b/src/test/java/com/google/javascript/clutz/double_inner_class_with_referenced_props.d.ts new file mode 100644 index 00000000..c1ab2251 --- /dev/null +++ b/src/test/java/com/google/javascript/clutz/double_inner_class_with_referenced_props.d.ts @@ -0,0 +1,22 @@ +declare namespace ಠ_ಠ.clutz { + class module$exports$ns$DoubleInnerClassWithRef { + private noStructuralTyping_module$exports$ns$DoubleInnerClassWithRef : any; + } +} +declare namespace ಠ_ಠ.clutz.module$exports$ns$DoubleInnerClassWithRef { + interface Inner { + baz (e1 : ಠ_ಠ.clutz.module$exports$ns$DoubleInnerClassWithRef.Inner.Enum1 , e2 : ಠ_ಠ.clutz.module$exports$ns$DoubleInnerClassWithRef.Inner.Enum2 ) : void ; + } +} +declare namespace ಠ_ಠ.clutz.module$exports$ns$DoubleInnerClassWithRef.Inner { + enum Enum1 { + FOO = 'foo' , + } + enum Enum2 { + BAR = 'bar' , + } +} +declare module 'goog:ns.DoubleInnerClassWithRef' { + import DoubleInnerClassWithRef = ಠ_ಠ.clutz.module$exports$ns$DoubleInnerClassWithRef; + export default DoubleInnerClassWithRef; +} diff --git a/src/test/java/com/google/javascript/clutz/double_inner_class_with_referenced_props.js b/src/test/java/com/google/javascript/clutz/double_inner_class_with_referenced_props.js new file mode 100644 index 00000000..ad75744f --- /dev/null +++ b/src/test/java/com/google/javascript/clutz/double_inner_class_with_referenced_props.js @@ -0,0 +1,30 @@ +goog.module('ns.DoubleInnerClassWithRef'); + +class A {} + +/** + * @interface + */ +A.Inner = function() {}; + +/** + * @enum {string} + */ +A.Inner.Enum1 = { + FOO: 'foo' +}; + +/** + * @enum {string} + */ +A.Inner.Enum2 = { + BAR: 'bar' +}; + +/** + * @param {A.Inner.Enum1} e1 + * @param {A.Inner.Enum2} e2 + */ +A.Inner.prototype.baz = function(e1, e2) {}; + +exports = A; \ No newline at end of file diff --git a/src/test/java/com/google/javascript/clutz/inner_class_of_private_class.d.ts b/src/test/java/com/google/javascript/clutz/inner_class_of_private_class.d.ts new file mode 100644 index 00000000..280e0192 --- /dev/null +++ b/src/test/java/com/google/javascript/clutz/inner_class_of_private_class.d.ts @@ -0,0 +1,22 @@ +declare namespace ಠ_ಠ.clutz.ns.inner_class_of_private_class { + class FooType_ { + private noStructuralTyping_ns_inner_class_of_private_class_FooType_ : any; + foo (inner : ಠ_ಠ.clutz.ns.inner_class_of_private_class.FooType_.Inner ) : void ; + } +} +declare module 'goog:ns.inner_class_of_private_class' { + import inner_class_of_private_class = ಠ_ಠ.clutz.ns.inner_class_of_private_class; + export = inner_class_of_private_class; +} +declare namespace ಠ_ಠ.clutz.ns.inner_class_of_private_class { + let FooInstance : ಠ_ಠ.clutz.ns.inner_class_of_private_class.FooType_ ; +} +declare module 'goog:ns.inner_class_of_private_class.FooInstance' { + import FooInstance = ಠ_ಠ.clutz.ns.inner_class_of_private_class.FooInstance; + export default FooInstance; +} +declare namespace ಠ_ಠ.clutz.ns.inner_class_of_private_class.FooType_ { + class Inner { + private noStructuralTyping_ns_inner_class_of_private_class_FooType__Inner : any; + } +} diff --git a/src/test/java/com/google/javascript/clutz/inner_class_of_private_class.js b/src/test/java/com/google/javascript/clutz/inner_class_of_private_class.js new file mode 100644 index 00000000..3d647da7 --- /dev/null +++ b/src/test/java/com/google/javascript/clutz/inner_class_of_private_class.js @@ -0,0 +1,26 @@ +goog.provide('ns.inner_class_of_private_class'); +goog.provide('ns.inner_class_of_private_class.FooInstance'); + +//!! This is a generalized code of `goog.debug.Trace`. +//!! https://github.com/google/closure-library/blob/master/closure/goog/debug/tracer.js + +/** + * @constructor + * @private + */ +ns.inner_class_of_private_class.FooType_ = function() {}; + +/** + * @param {!ns.inner_class_of_private_class.FooType_.Inner} inner + */ +ns.inner_class_of_private_class.FooType_.prototype.foo = function(inner) {}; + +/** + * @constructor + */ +ns.inner_class_of_private_class.FooType_.Inner = function() {}; + +/** + * @type {!ns.inner_class_of_private_class.FooType_} + */ +ns.inner_class_of_private_class.FooInstance = new ns.inner_class_of_private_class.FooType_();